Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Mon, 10 Dec 2018 05:27:48 +0000 (21:27 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 10 Dec 2018 05:43:31 +0000 (21:43 -0800)
Several conflicts, seemingly all over the place.

I used Stephen Rothwell's sample resolutions for many of these, if not
just to double check my own work, so definitely the credit largely
goes to him.

The NFP conflict consisted of a bug fix (moving operations
past the rhashtable operation) while chaning the initial
argument in the function call in the moved code.

The net/dsa/master.c conflict had to do with a bug fix intermixing of
making dsa_master_set_mtu() static with the fixing of the tagging
attribute location.

cls_flower had a conflict because the dup reject fix from Or
overlapped with the addition of port range classifiction.

__set_phy_supported()'s conflict was relatively easy to resolve
because Andrew fixed it in both trees, so it was just a matter
of taking the net-next copy.  Or at least I think it was :-)

Joe Stringer's fix to the handling of netns id 0 in bpf_sk_lookup()
intermixed with changes on how the sdif and caller_net are calculated
in these code paths in net-next.

The remaining BPF conflicts were largely about the addition of the
__bpf_md_ptr stuff in 'net' overlapping with adjustments and additions
to the relevant data structure where the MD pointer macros are used.

Signed-off-by: David S. Miller <davem@davemloft.net>
1035 files changed:
Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
Documentation/devicetree/bindings/net/can/xilinx_can.txt
Documentation/devicetree/bindings/net/icplus-ip101ag.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/networking/device_drivers/3com/3c509.txt [moved from Documentation/networking/3c509.txt with 100% similarity]
Documentation/networking/device_drivers/3com/vortex.txt [moved from Documentation/networking/vortex.txt with 99% similarity]
Documentation/networking/device_drivers/amazon/ena.txt [moved from Documentation/networking/ena.txt with 100% similarity]
Documentation/networking/device_drivers/chelsio/cxgb.txt [moved from Documentation/networking/cxgb.txt with 100% similarity]
Documentation/networking/device_drivers/cirrus/cs89x0.txt [moved from Documentation/networking/cs89x0.txt with 100% similarity]
Documentation/networking/device_drivers/davicom/dm9000.txt [moved from Documentation/networking/dm9000.txt with 100% similarity]
Documentation/networking/device_drivers/dec/de4x5.txt [moved from Documentation/networking/de4x5.txt with 100% similarity]
Documentation/networking/device_drivers/dec/dmfe.txt [moved from Documentation/networking/dmfe.txt with 100% similarity]
Documentation/networking/device_drivers/dlink/dl2k.txt [moved from Documentation/networking/dl2k.txt with 100% similarity]
Documentation/networking/device_drivers/freescale/dpaa.txt [moved from Documentation/networking/dpaa.txt with 100% similarity]
Documentation/networking/device_drivers/freescale/dpaa2/dpio-driver.rst [moved from Documentation/networking/dpaa2/dpio-driver.rst with 97% similarity]
Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst [moved from Documentation/networking/dpaa2/ethernet-driver.rst with 98% similarity]
Documentation/networking/device_drivers/freescale/dpaa2/index.rst [moved from Documentation/networking/dpaa2/index.rst with 100% similarity]
Documentation/networking/device_drivers/freescale/dpaa2/overview.rst [moved from Documentation/networking/dpaa2/overview.rst with 100% similarity]
Documentation/networking/device_drivers/freescale/gianfar.txt [moved from Documentation/networking/gianfar.txt with 100% similarity]
Documentation/networking/device_drivers/intel/e100.rst [moved from Documentation/networking/e100.rst with 100% similarity]
Documentation/networking/device_drivers/intel/e1000.rst [moved from Documentation/networking/e1000.rst with 100% similarity]
Documentation/networking/device_drivers/intel/e1000e.rst [moved from Documentation/networking/e1000e.rst with 100% similarity]
Documentation/networking/device_drivers/intel/fm10k.rst [moved from Documentation/networking/fm10k.rst with 100% similarity]
Documentation/networking/device_drivers/intel/i40e.rst [moved from Documentation/networking/i40e.rst with 100% similarity]
Documentation/networking/device_drivers/intel/iavf.rst [moved from Documentation/networking/iavf.rst with 100% similarity]
Documentation/networking/device_drivers/intel/ice.rst [moved from Documentation/networking/ice.rst with 100% similarity]
Documentation/networking/device_drivers/intel/igb.rst [moved from Documentation/networking/igb.rst with 100% similarity]
Documentation/networking/device_drivers/intel/igbvf.rst [moved from Documentation/networking/igbvf.rst with 100% similarity]
Documentation/networking/device_drivers/intel/ipw2100.txt [moved from Documentation/networking/README.ipw2100 with 100% similarity]
Documentation/networking/device_drivers/intel/ipw2200.txt [moved from Documentation/networking/README.ipw2200 with 100% similarity]
Documentation/networking/device_drivers/intel/ixgb.rst [moved from Documentation/networking/ixgb.rst with 100% similarity]
Documentation/networking/device_drivers/intel/ixgbe.rst [moved from Documentation/networking/ixgbe.rst with 97% similarity]
Documentation/networking/device_drivers/intel/ixgbevf.rst [moved from Documentation/networking/ixgbevf.rst with 100% similarity]
Documentation/networking/device_drivers/microsoft/netvsc.txt [moved from Documentation/networking/netvsc.txt with 100% similarity]
Documentation/networking/device_drivers/neterion/s2io.txt [moved from Documentation/networking/s2io.txt with 100% similarity]
Documentation/networking/device_drivers/neterion/vxge.txt [moved from Documentation/networking/vxge.txt with 100% similarity]
Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx [moved from Documentation/networking/LICENSE.qla3xxx with 100% similarity]
Documentation/networking/device_drivers/qlogic/LICENSE.qlcnic [moved from Documentation/networking/LICENSE.qlcnic with 100% similarity]
Documentation/networking/device_drivers/qlogic/LICENSE.qlge [moved from Documentation/networking/LICENSE.qlge with 100% similarity]
Documentation/networking/device_drivers/qualcomm/rmnet.txt [moved from Documentation/networking/rmnet.txt with 100% similarity]
Documentation/networking/device_drivers/sb1000.txt [moved from Documentation/networking/README.sb1000 with 100% similarity]
Documentation/networking/device_drivers/smsc/smc9.txt [moved from Documentation/networking/smc9.txt with 100% similarity]
Documentation/networking/device_drivers/stmicro/stmmac.txt [moved from Documentation/networking/stmmac.txt with 100% similarity]
Documentation/networking/device_drivers/ti/cpsw.txt [moved from Documentation/networking/ti-cpsw.txt with 100% similarity]
Documentation/networking/device_drivers/ti/tlan.txt [moved from Documentation/networking/tlan.txt with 100% similarity]
Documentation/networking/device_drivers/toshiba/spider_net.txt [moved from Documentation/networking/spider_net.txt with 100% similarity]
Documentation/networking/devlink-params.txt
Documentation/networking/index.rst
Documentation/networking/ip-sysctl.txt
Documentation/networking/netdev-features.txt
Documentation/networking/snmp_counter.rst [new file with mode: 0644]
Documentation/networking/vrf.txt
MAINTAINERS
arch/mips/net/bpf_jit.c
arch/powerpc/net/bpf_jit_comp.c
arch/sparc/net/bpf_jit_comp_32.c
drivers/atm/fore200e.c
drivers/crypto/chelsio/chtls/chtls_cm.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/i40iw/i40iw_cm.c
drivers/infiniband/hw/mlx4/cq.c
drivers/infiniband/hw/nes/nes_mgt.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/isdn/hardware/Kconfig
drivers/isdn/hardware/Makefile
drivers/isdn/hardware/eicon/Kconfig [deleted file]
drivers/isdn/hardware/eicon/Makefile [deleted file]
drivers/isdn/hardware/eicon/adapter.h [deleted file]
drivers/isdn/hardware/eicon/capi20.h [deleted file]
drivers/isdn/hardware/eicon/capidtmf.c [deleted file]
drivers/isdn/hardware/eicon/capidtmf.h [deleted file]
drivers/isdn/hardware/eicon/capifunc.c [deleted file]
drivers/isdn/hardware/eicon/capifunc.h [deleted file]
drivers/isdn/hardware/eicon/capimain.c [deleted file]
drivers/isdn/hardware/eicon/cardtype.h [deleted file]
drivers/isdn/hardware/eicon/cp_vers.h [deleted file]
drivers/isdn/hardware/eicon/dadapter.c [deleted file]
drivers/isdn/hardware/eicon/dadapter.h [deleted file]
drivers/isdn/hardware/eicon/debug.c [deleted file]
drivers/isdn/hardware/eicon/debug_if.h [deleted file]
drivers/isdn/hardware/eicon/debuglib.c [deleted file]
drivers/isdn/hardware/eicon/debuglib.h [deleted file]
drivers/isdn/hardware/eicon/dfifo.h [deleted file]
drivers/isdn/hardware/eicon/di.c [deleted file]
drivers/isdn/hardware/eicon/di.h [deleted file]
drivers/isdn/hardware/eicon/di_dbg.h [deleted file]
drivers/isdn/hardware/eicon/di_defs.h [deleted file]
drivers/isdn/hardware/eicon/did_vers.h [deleted file]
drivers/isdn/hardware/eicon/diddfunc.c [deleted file]
drivers/isdn/hardware/eicon/diva.c [deleted file]
drivers/isdn/hardware/eicon/diva.h [deleted file]
drivers/isdn/hardware/eicon/diva_didd.c [deleted file]
drivers/isdn/hardware/eicon/diva_dma.c [deleted file]
drivers/isdn/hardware/eicon/diva_dma.h [deleted file]
drivers/isdn/hardware/eicon/diva_pci.h [deleted file]
drivers/isdn/hardware/eicon/divacapi.h [deleted file]
drivers/isdn/hardware/eicon/divamnt.c [deleted file]
drivers/isdn/hardware/eicon/divasfunc.c [deleted file]
drivers/isdn/hardware/eicon/divasi.c [deleted file]
drivers/isdn/hardware/eicon/divasmain.c [deleted file]
drivers/isdn/hardware/eicon/divasproc.c [deleted file]
drivers/isdn/hardware/eicon/divasync.h [deleted file]
drivers/isdn/hardware/eicon/dqueue.c [deleted file]
drivers/isdn/hardware/eicon/dqueue.h [deleted file]
drivers/isdn/hardware/eicon/dsp_defs.h [deleted file]
drivers/isdn/hardware/eicon/dsp_tst.h [deleted file]
drivers/isdn/hardware/eicon/dspdids.h [deleted file]
drivers/isdn/hardware/eicon/dsrv4bri.h [deleted file]
drivers/isdn/hardware/eicon/dsrv_bri.h [deleted file]
drivers/isdn/hardware/eicon/dsrv_pri.h [deleted file]
drivers/isdn/hardware/eicon/entity.h [deleted file]
drivers/isdn/hardware/eicon/helpers.h [deleted file]
drivers/isdn/hardware/eicon/idifunc.c [deleted file]
drivers/isdn/hardware/eicon/io.c [deleted file]
drivers/isdn/hardware/eicon/io.h [deleted file]
drivers/isdn/hardware/eicon/istream.c [deleted file]
drivers/isdn/hardware/eicon/kst_ifc.h [deleted file]
drivers/isdn/hardware/eicon/maintidi.c [deleted file]
drivers/isdn/hardware/eicon/maintidi.h [deleted file]
drivers/isdn/hardware/eicon/man_defs.h [deleted file]
drivers/isdn/hardware/eicon/mdm_msg.h [deleted file]
drivers/isdn/hardware/eicon/message.c [deleted file]
drivers/isdn/hardware/eicon/mi_pc.h [deleted file]
drivers/isdn/hardware/eicon/mntfunc.c [deleted file]
drivers/isdn/hardware/eicon/os_4bri.c [deleted file]
drivers/isdn/hardware/eicon/os_4bri.h [deleted file]
drivers/isdn/hardware/eicon/os_bri.c [deleted file]
drivers/isdn/hardware/eicon/os_bri.h [deleted file]
drivers/isdn/hardware/eicon/os_capi.h [deleted file]
drivers/isdn/hardware/eicon/os_pri.c [deleted file]
drivers/isdn/hardware/eicon/os_pri.h [deleted file]
drivers/isdn/hardware/eicon/pc.h [deleted file]
drivers/isdn/hardware/eicon/pc_init.h [deleted file]
drivers/isdn/hardware/eicon/pc_maint.h [deleted file]
drivers/isdn/hardware/eicon/pkmaint.h [deleted file]
drivers/isdn/hardware/eicon/platform.h [deleted file]
drivers/isdn/hardware/eicon/pr_pc.h [deleted file]
drivers/isdn/hardware/eicon/s_4bri.c [deleted file]
drivers/isdn/hardware/eicon/s_bri.c [deleted file]
drivers/isdn/hardware/eicon/s_pri.c [deleted file]
drivers/isdn/hardware/eicon/sdp_hdr.h [deleted file]
drivers/isdn/hardware/eicon/um_idi.c [deleted file]
drivers/isdn/hardware/eicon/um_idi.h [deleted file]
drivers/isdn/hardware/eicon/um_xdi.h [deleted file]
drivers/isdn/hardware/eicon/xdi_adapter.h [deleted file]
drivers/isdn/hardware/eicon/xdi_msg.h [deleted file]
drivers/isdn/hardware/eicon/xdi_vers.h [deleted file]
drivers/isdn/hardware/mISDN/w6692.c
drivers/isdn/hisax/hfc_pci.c
drivers/misc/mic/vop/vop_main.c
drivers/net/Kconfig
drivers/net/bonding/bond_main.c
drivers/net/can/Kconfig
drivers/net/can/flexcan.c
drivers/net/can/rcar/Kconfig
drivers/net/can/rcar/Makefile
drivers/net/can/rcar/rcar_can.c
drivers/net/can/rcar/rcar_canfd.c
drivers/net/can/sja1000/Kconfig
drivers/net/can/sja1000/plx_pci.c
drivers/net/can/usb/ucan.c
drivers/net/can/vxcan.c
drivers/net/can/xilinx_can.c
drivers/net/dsa/bcm_sf2.c
drivers/net/dsa/bcm_sf2.h
drivers/net/dsa/bcm_sf2_cfp.c
drivers/net/dsa/microchip/Kconfig
drivers/net/dsa/microchip/Makefile
drivers/net/dsa/microchip/ksz9477.c [new file with mode: 0644]
drivers/net/dsa/microchip/ksz9477_reg.h [moved from drivers/net/dsa/microchip/ksz_9477_reg.h with 98% similarity]
drivers/net/dsa/microchip/ksz9477_spi.c [new file with mode: 0644]
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h [new file with mode: 0644]
drivers/net/dsa/microchip/ksz_priv.h
drivers/net/dsa/microchip/ksz_spi.c [deleted file]
drivers/net/dsa/microchip/ksz_spi.h [new file with mode: 0644]
drivers/net/dsa/mt7530.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/port.c
drivers/net/dsa/mv88e6xxx/port.h
drivers/net/dsa/mv88e6xxx/serdes.c
drivers/net/dsa/mv88e6xxx/serdes.h
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/3com/Kconfig
drivers/net/ethernet/aeroflex/greth.c
drivers/net/ethernet/amd/sunlance.c
drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
drivers/net/ethernet/apm/xgene-v2/mdio.c
drivers/net/ethernet/aquantia/atlantic/Makefile
drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
drivers/net/ethernet/aquantia/atlantic/aq_common.h
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
drivers/net/ethernet/aquantia/atlantic/aq_filters.c [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/aq_filters.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_main.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.h
drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
drivers/net/ethernet/arc/emac_main.c
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bcmsysport.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/cnic.c
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cavium/common/cavium_ptp.c
drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
drivers/net/ethernet/chelsio/Kconfig
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/l2t.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/cirrus/Kconfig
drivers/net/ethernet/cisco/enic/enic_ethtool.c
drivers/net/ethernet/dec/tulip/Kconfig
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c
drivers/net/ethernet/freescale/fman/mac.c
drivers/net/ethernet/freescale/fsl_pq_mdio.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/gianfar_ethtool.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
drivers/net/ethernet/hisilicon/hns3/Makefile
drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c
drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_port.c
drivers/net/ethernet/huawei/hinic/hinic_port.h
drivers/net/ethernet/huawei/hinic/hinic_rx.c
drivers/net/ethernet/huawei/hinic/hinic_rx.h
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/intel/Kconfig
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/ptp.c
drivers/net/ethernet/intel/fm10k/fm10k_main.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
drivers/net/ethernet/intel/iavf/iavf_txrx.c
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_common.c
drivers/net/ethernet/intel/ice/ice_controlq.c
drivers/net/ethernet/intel/ice/ice_ethtool.c
drivers/net/ethernet/intel/ice/ice_hw_autogen.h
drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_sched.c
drivers/net/ethernet/intel/ice/ice_sched.h
drivers/net/ethernet/intel/ice/ice_sriov.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_txrx.c
drivers/net/ethernet/intel/ice/ice_type.h
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/intel/igbvf/mbx.c
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_base.c
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/octeontx2/af/cgx.c
drivers/net/ethernet/marvell/octeontx2/af/cgx.h
drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h
drivers/net/ethernet/marvell/octeontx2/af/common.h
drivers/net/ethernet/marvell/octeontx2/af/mbox.c
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/npc.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.c
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
drivers/net/ethernet/marvell/skge.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mellanox/mlx4/cq.c
drivers/net/ethernet/mellanox/mlx4/en_cq.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/fw.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
drivers/net/ethernet/mellanox/mlxsw/Kconfig
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.c
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
drivers/net/ethernet/mellanox/mlxsw/pci.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_ctcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_board.c
drivers/net/ethernet/neterion/Kconfig
drivers/net/ethernet/neterion/vxge/vxge-traffic.c
drivers/net/ethernet/netronome/nfp/Makefile
drivers/net/ethernet/netronome/nfp/abm/cls.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/abm/ctrl.c
drivers/net/ethernet/netronome/nfp/abm/main.c
drivers/net/ethernet/netronome/nfp/abm/main.h
drivers/net/ethernet/netronome/nfp/abm/qdisc.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/bpf/main.c
drivers/net/ethernet/netronome/nfp/bpf/main.h
drivers/net/ethernet/netronome/nfp/bpf/offload.c
drivers/net/ethernet/netronome/nfp/bpf/verifier.c
drivers/net/ethernet/netronome/nfp/flower/action.c
drivers/net/ethernet/netronome/nfp/flower/cmsg.h
drivers/net/ethernet/netronome/nfp/flower/lag_conf.c
drivers/net/ethernet/netronome/nfp/flower/main.c
drivers/net/ethernet/netronome/nfp/flower/main.h
drivers/net/ethernet/netronome/nfp/flower/match.c
drivers/net/ethernet/netronome/nfp/flower/metadata.c
drivers/net/ethernet/netronome/nfp/flower/offload.c
drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
drivers/net/ethernet/netronome/nfp/nfp_app.c
drivers/net/ethernet/netronome/nfp/nfp_app.h
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
drivers/net/ethernet/netronome/nfp/nfp_net_main.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.h
drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_dev_api.h
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_int.c
drivers/net/ethernet/qlogic/qed/qed_int.h
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_ll2.h
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_mcp.c
drivers/net/ethernet/qlogic/qed/qed_mcp.h
drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_ethtool.c
drivers/net/ethernet/qlogic/qede/qede_fp.c
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qede/qede_ptp.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qualcomm/qca_debug.c
drivers/net/ethernet/qualcomm/qca_spi.c
drivers/net/ethernet/qualcomm/qca_spi.h
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/ravb.h
drivers/net/ethernet/renesas/ravb_main.c
drivers/net/ethernet/rocker/rocker_main.c
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/falcon/ethtool.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/socionext/netsec.c
drivers/net/ethernet/socionext/sni_ave.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/ti/cpts.h
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/netcp_ethss.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/toshiba/tc35815.c
drivers/net/geneve.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/ipvlan/ipvlan_main.c
drivers/net/net_failover.c
drivers/net/netdevsim/bpf.c
drivers/net/phy/amd.c
drivers/net/phy/aquantia.c
drivers/net/phy/at803x.c
drivers/net/phy/bcm63xx.c
drivers/net/phy/bcm7xxx.c
drivers/net/phy/bcm87xx.c
drivers/net/phy/broadcom.c
drivers/net/phy/cicada.c
drivers/net/phy/davicom.c
drivers/net/phy/dp83640.c
drivers/net/phy/dp83822.c
drivers/net/phy/dp83848.c
drivers/net/phy/dp83867.c
drivers/net/phy/dp83tc811.c
drivers/net/phy/fixed_phy.c
drivers/net/phy/icplus.c
drivers/net/phy/intel-xway.c
drivers/net/phy/lxt.c
drivers/net/phy/marvell.c
drivers/net/phy/marvell10g.c
drivers/net/phy/mdio-gpio.c
drivers/net/phy/meson-gxl.c
drivers/net/phy/micrel.c
drivers/net/phy/microchip.c
drivers/net/phy/microchip_t1.c
drivers/net/phy/mscc.c
drivers/net/phy/national.c
drivers/net/phy/phy-c45.c
drivers/net/phy/phy-core.c
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/phy_led_triggers.c
drivers/net/phy/phylink.c
drivers/net/phy/qsemi.c
drivers/net/phy/realtek.c
drivers/net/phy/smsc.c
drivers/net/phy/ste10Xp.c
drivers/net/phy/uPD60620.c
drivers/net/phy/vitesse.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/Makefile
drivers/net/usb/aqc111.c [new file with mode: 0644]
drivers/net/usb/aqc111.h [new file with mode: 0644]
drivers/net/usb/cdc_ether.c
drivers/net/usb/lan78xx.c
drivers/net/usb/smsc95xx.c
drivers/net/veth.c
drivers/net/vrf.c
drivers/net/vxlan.c
drivers/net/wan/fsl_ucc_hdlc.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/coredump.c
drivers/net/wireless/ath/ath10k/coredump.h
drivers/net/wireless/ath/ath10k/debugfs_sta.c
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/qmi.c
drivers/net/wireless/ath/ath10k/snoc.c
drivers/net/wireless/ath/ath10k/snoc.h
drivers/net/wireless/ath/ath10k/wmi-tlv.c
drivers/net/wireless/ath/ath10k/wmi-tlv.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath10k/wow.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/dynack.c
drivers/net/wireless/ath/ath9k/dynack.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/rx.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx_edma.c
drivers/net/wireless/ath/wil6210/txrx_edma.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/broadcom/b43/Kconfig
drivers/net/wireless/broadcom/b43/phy_common.c
drivers/net/wireless/broadcom/b43/phy_common.h
drivers/net/wireless/broadcom/b43/phy_lp.c
drivers/net/wireless/broadcom/b43/phy_n.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c [new file with mode: 0644]
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
drivers/net/wireless/cisco/airo.c
drivers/net/wireless/intel/ipw2x00/Kconfig
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intel/iwlegacy/3945-rs.c
drivers/net/wireless/intel/iwlegacy/4965-mac.c
drivers/net/wireless/intel/iwlegacy/common.c
drivers/net/wireless/intel/iwlwifi/Makefile
drivers/net/wireless/intel/iwlwifi/cfg/22000.c
drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
drivers/net/wireless/intel/iwlwifi/fw/dbg.c
drivers/net/wireless/intel/iwlwifi/fw/dbg.h
drivers/net/wireless/intel/iwlwifi/fw/file.h
drivers/net/wireless/intel/iwlwifi/fw/img.h
drivers/net/wireless/intel/iwlwifi/fw/runtime.h
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h [new file with mode: 0644]
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
drivers/net/wireless/intel/iwlwifi/iwl-prph.h
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
drivers/net/wireless/intel/iwlwifi/mvm/scan.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.h
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
drivers/net/wireless/intel/iwlwifi/mvm/utils.c
drivers/net/wireless/intel/iwlwifi/pcie/drv.c
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/trans.c
drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
drivers/net/wireless/intersil/hostap/hostap_main.c
drivers/net/wireless/intersil/orinoco/orinoco_usb.c
drivers/net/wireless/intersil/prism54/isl_38xx.c
drivers/net/wireless/intersil/prism54/isl_ioctl.c
drivers/net/wireless/intersil/prism54/islpci_dev.c
drivers/net/wireless/marvell/libertas/if_spi.c
drivers/net/wireless/marvell/mwifiex/ie.c
drivers/net/wireless/quantenna/qtnfmac/Kconfig
drivers/net/wireless/quantenna/qtnfmac/Makefile
drivers/net/wireless/quantenna/qtnfmac/commands.c
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
drivers/net/wireless/quantenna/qtnfmac/pcie/pearl_pcie.c
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h [new file with mode: 0644]
drivers/net/wireless/quantenna/qtnfmac/qtn_hw_ids.h
drivers/net/wireless/quantenna/qtnfmac/util.c
drivers/net/wireless/quantenna/qtnfmac/util.h
drivers/net/wireless/ralink/rt2x00/rt2400pci.c
drivers/net/wireless/ralink/rt2x00/rt2500pci.c
drivers/net/wireless/ralink/rt2x00/rt2800lib.c
drivers/net/wireless/ralink/rt2x00/rt61pci.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c
drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c
drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/phy.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.c
drivers/net/wireless/realtek/rtlwifi/rtl8723ae/table.h
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c
drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
drivers/net/wireless/st/cw1200/sta.c
drivers/net/wireless/ti/wlcore/vendor_cmd.c
drivers/net/wireless/zydas/zd1201.c
drivers/net/xen-netfront.c
drivers/of/of_net.c
drivers/ptp/ptp_chardev.c
drivers/ptp/ptp_clock.c
drivers/remoteproc/remoteproc_virtio.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_mpc.c
drivers/s390/net/qeth_core_mpc.h
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/s390/virtio/virtio_ccw.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/soc/fsl/qbman/qman.c
drivers/staging/fsl-dpaa2/ethsw/ethsw.c
drivers/staging/unisys/visornic/visornic_main.c
drivers/target/iscsi/cxgbit/cxgbit_cm.c
drivers/vhost/net.c
drivers/virtio/virtio_ring.c
include/linux/avf/virtchnl.h
include/linux/bpf.h
include/linux/bpf_verifier.h
include/linux/brcmphy.h
include/linux/btf.h
include/linux/cordic.h
include/linux/etherdevice.h
include/linux/filter.h
include/linux/if_bridge.h
include/linux/if_vlan.h
include/linux/linkmode.h
include/linux/mii.h
include/linux/mlx4/device.h
include/linux/netdevice.h
include/linux/objagg.h [new file with mode: 0644]
include/linux/of_net.h
include/linux/pci_ids.h
include/linux/phy.h
include/linux/phy_led_triggers.h
include/linux/platform_data/mdio-gpio.h [new file with mode: 0644]
include/linux/ptp_clock_kernel.h
include/linux/qed/qed_if.h
include/linux/rhashtable.h
include/linux/skbuff.h
include/linux/udp.h
include/net/devlink.h
include/net/dsa.h
include/net/flow.h
include/net/flow_dissector.h
include/net/gen_stats.h
include/net/geneve.h
include/net/icmp.h
include/net/inet6_hashtables.h
include/net/inet_hashtables.h
include/net/inet_sock.h
include/net/ip.h
include/net/ip6_tunnel.h
include/net/ip_tunnels.h
include/net/ipv6.h
include/net/l3mdev.h
include/net/neighbour.h
include/net/netns/ipv4.h
include/net/pkt_cls.h
include/net/protocol.h
include/net/raw.h
include/net/rtnetlink.h
include/net/sch_generic.h
include/net/sctp/constants.h
include/net/sctp/sctp.h
include/net/sctp/sm.h
include/net/sctp/structs.h
include/net/sctp/ulpevent.h
include/net/sock.h
include/net/switchdev.h
include/net/tcp.h
include/net/udp.h
include/net/udp_tunnel.h
include/net/vxlan.h
include/soc/fsl/qman.h
include/trace/events/net.h
include/trace/events/objagg.h [new file with mode: 0644]
include/uapi/linux/bpf.h
include/uapi/linux/btf.h
include/uapi/linux/devlink.h
include/uapi/linux/ethtool.h
include/uapi/linux/if_bridge.h
include/uapi/linux/if_link.h
include/uapi/linux/if_tun.h
include/uapi/linux/ncsi.h
include/uapi/linux/net_namespace.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/ptp_clock.h
include/uapi/linux/sctp.h
include/uapi/linux/snmp.h
include/uapi/linux/tcp.h
include/uapi/linux/udp.h
include/uapi/linux/virtio_config.h
include/uapi/linux/virtio_ring.h
kernel/bpf/btf.c
kernel/bpf/core.c
kernel/bpf/hashtab.c
kernel/bpf/local_storage.c
kernel/bpf/lpm_trie.c
kernel/bpf/offload.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/cordic.c
lib/objagg.c [new file with mode: 0644]
lib/rhashtable.c
lib/test_bpf.c
lib/test_objagg.c [new file with mode: 0644]
net/8021q/vlan.c
net/8021q/vlan.h
net/8021q/vlan_core.c
net/8021q/vlan_dev.c
net/batman-adv/Kconfig
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bat_v.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/hash.c
net/batman-adv/hash.h
net/batman-adv/log.c
net/batman-adv/main.c
net/batman-adv/main.h
net/batman-adv/multicast.c
net/batman-adv/netlink.c
net/batman-adv/trace.c
net/batman-adv/trace.h
net/batman-adv/translation-table.c
net/batman-adv/types.h
net/bluetooth/6lowpan.c
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_netfilter_hooks.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_if.c
net/bridge/br_vlan.c
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/dev_ioctl.c
net/core/devlink.c
net/core/filter.c
net/core/flow_dissector.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/net_namespace.c
net/core/netpoll.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/core/sock_reuseport.c
net/core/stream.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/dccp/proto.c
net/decnet/af_decnet.c
net/dsa/master.c
net/dsa/port.c
net/dsa/slave.c
net/dsa/tag_brcm.c
net/dsa/tag_dsa.c
net/dsa/tag_edsa.c
net/dsa/tag_gswip.c
net/dsa/tag_ksz.c
net/dsa/tag_lan9303.c
net/dsa/tag_mtk.c
net/dsa/tag_qca.c
net/dsa/tag_trailer.c
net/ethernet/eth.c
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/fib_semantics.c
net/ipv4/fou.c
net/ipv4/gre_demux.c
net/ipv4/icmp.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_hashtables.c
net/ipv4/ip_forward.c
net/ipv4/ip_gre.c
net/ipv4/ip_input.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ipconfig.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/metrics.c
net/ipv4/proc.c
net/ipv4/protocol.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_bbr.c
net/ipv4/tcp_bpf.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv4/tunnel4.c
net/ipv4/udp.c
net/ipv4/udp_impl.h
net/ipv4/udp_offload.c
net/ipv4/udp_tunnel.c
net/ipv4/udplite.c
net/ipv4/xfrm4_protocol.c
net/ipv6/addrconf.c
net/ipv6/anycast.c
net/ipv6/datagram.c
net/ipv6/fou6.c
net/ipv6/icmp.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_input.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_udp_tunnel.c
net/ipv6/ip6mr.c
net/ipv6/ipv6_sockglue.c
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/ipv6/tunnel6.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udp_offload.c
net/ipv6/udplite.c
net/ipv6/xfrm6_protocol.c
net/iucv/af_iucv.c
net/l3mdev/l3mdev.c
net/ncsi/internal.h
net/ncsi/ncsi-aen.c
net/ncsi/ncsi-manage.c
net/ncsi/ncsi-netlink.c
net/ncsi/ncsi-pkt.h
net/ncsi/ncsi-rsp.c
net/netfilter/nfnetlink_queue.c
net/openvswitch/actions.c
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c
net/openvswitch/vport-geneve.c
net/openvswitch/vport-gre.c
net/openvswitch/vport-netdev.c
net/openvswitch/vport-vxlan.c
net/sched/act_tunnel_key.c
net/sched/act_vlan.c
net/sched/cls_api.c
net/sched/cls_flower.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_etf.c
net/sched/sch_fq.c
net/sched/sch_gred.c
net/sched/sch_mq.c
net/sched/sch_netem.c
net/sched/sch_prio.c
net/sched/sch_red.c
net/sctp/associola.c
net/sctp/bind_addr.c
net/sctp/chunk.c
net/sctp/input.c
net/sctp/ipv6.c
net/sctp/primitive.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statetable.c
net/sctp/socket.c
net/sctp/stream_interleave.c
net/sctp/ulpqueue.c
net/smc/af_smc.c
net/smc/smc_clc.c
net/smc/smc_clc.h
net/smc/smc_core.c
net/smc/smc_core.h
net/smc/smc_llc.c
net/smc/smc_llc.h
net/sunrpc/socklib.c
net/switchdev/switchdev.c
net/tipc/link.c
net/tipc/msg.h
net/tipc/node.c
net/tls/tls_sw.c
samples/bpf/Makefile
samples/bpf/bpf_load.c
tools/bpf/bpftool/Documentation/bpftool-map.rst
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/Makefile
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/btf_dumper.c
tools/bpf/bpftool/common.c
tools/bpf/bpftool/jit_disasm.c
tools/bpf/bpftool/main.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/map.c
tools/bpf/bpftool/prog.c
tools/bpf/bpftool/xlated_dumper.c
tools/bpf/bpftool/xlated_dumper.h
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/btf.h
tools/lib/bpf/Makefile
tools/lib/bpf/README.rst [new file with mode: 0644]
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/btf.c
tools/lib/bpf/btf.h
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map [new file with mode: 0644]
tools/lib/bpf/libbpf_errno.c
tools/lib/bpf/test_libbpf.cpp [new file with mode: 0644]
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_flow.c
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/config
tools/testing/selftests/bpf/connect4_prog.c
tools/testing/selftests/bpf/connect6_prog.c
tools/testing/selftests/bpf/test_btf.c
tools/testing/selftests/bpf/test_btf_haskv.c
tools/testing/selftests/bpf/test_btf_nokv.c
tools/testing/selftests/bpf/test_flow_dissector.sh
tools/testing/selftests/bpf/test_libbpf.sh
tools/testing/selftests/bpf/test_map_in_map.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_sock_addr.c
tools/testing/selftests/bpf/test_sockmap.c
tools/testing/selftests/bpf/test_sockmap_kern.h
tools/testing/selftests/bpf/test_tcpnotify.h [new file with mode: 0644]
tools/testing/selftests/bpf/test_tcpnotify_kern.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_tcpnotify_user.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/bpf/xdp_dummy.c [new file with mode: 0644]
tools/testing/selftests/drivers/net/mlxsw/extack.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/spectrum-2/tc_flower.sh
tools/testing/selftests/drivers/net/mlxsw/vxlan.sh [new file with mode: 0755]
tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh [new file with mode: 0755]
tools/testing/selftests/net/.gitignore
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/config
tools/testing/selftests/net/forwarding/lib.sh
tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh [new file with mode: 0755]
tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh [new file with mode: 0755]
tools/testing/selftests/net/msg_zerocopy.c
tools/testing/selftests/net/msg_zerocopy.sh
tools/testing/selftests/net/pmtu.sh
tools/testing/selftests/net/run_afpackettests
tools/testing/selftests/net/test_vxlan_under_vrf.sh [new file with mode: 0755]
tools/testing/selftests/net/txring_overwrite.c [new file with mode: 0644]
tools/testing/selftests/net/udpgro.sh [new file with mode: 0755]
tools/testing/selftests/net/udpgro_bench.sh [new file with mode: 0755]
tools/testing/selftests/net/udpgso_bench.sh
tools/testing/selftests/net/udpgso_bench_rx.c
tools/testing/selftests/net/udpgso_bench_tx.c
tools/testing/selftests/tc-testing/.gitignore
tools/testing/selftests/tc-testing/TdcPlugin.py
tools/testing/selftests/tc-testing/TdcResults.py [new file with mode: 0644]
tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py
tools/testing/selftests/tc-testing/tdc.py
tools/testing/selftests/tc-testing/tdc_config.py

index 01fdc33..bb7e896 100644 (file)
@@ -10,7 +10,7 @@ such as network interfaces, crypto accelerator instances, L2 switches,
 etc.
 
 For an overview of the DPAA2 architecture and fsl-mc bus see:
-Documentation/networking/dpaa2/overview.rst
+Documentation/networking/device_drivers/freescale/dpaa2/overview.rst
 
 As described in the above overview, all DPAA2 objects in a DPRC share the
 same hardware "isolation context" and a 10-bit value called an ICID
index bfc0c43..bc77477 100644 (file)
@@ -24,6 +24,14 @@ Optional properties:
               if this property is present then controller is assumed to be big
               endian.
 
+- fsl,stop-mode: register bits of stop mode control, the format is
+                <&gpr req_gpr req_bit ack_gpr ack_bit>.
+                gpr is the phandle to general purpose register node.
+                req_gpr is the gpr register offset of CAN stop request.
+                req_bit is the bit offset of CAN stop request.
+                ack_gpr is the gpr register offset of CAN stop acknowledge.
+                ack_bit is the bit offset of CAN stop acknowledge.
+
 Example:
 
        can@1c000 {
index 060e2d4..100cc40 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
                          - "xlnx,zynq-can-1.0" for Zynq CAN controllers
                          - "xlnx,axi-can-1.00.a" for Axi CAN controllers
                          - "xlnx,canfd-1.0" for CAN FD controllers
+                         - "xlnx,canfd-2.0" for CAN FD 2.0 controllers
 - reg                  : Physical base address and size of the controller
                          registers map.
 - interrupts           : Property with a value describing the interrupt
diff --git a/Documentation/devicetree/bindings/net/icplus-ip101ag.txt b/Documentation/devicetree/bindings/net/icplus-ip101ag.txt
new file mode 100644 (file)
index 0000000..a784592
--- /dev/null
@@ -0,0 +1,19 @@
+IC Plus Corp. IP101A / IP101G Ethernet PHYs
+
+There are different models of the IP101G Ethernet PHY:
+- IP101GR (32-pin QFN package)
+- IP101G (die only, no package)
+- IP101GA (48-pin LQFP package)
+
+There are different models of the IP101A Ethernet PHY (which is the
+predecessor of the IP101G):
+- IP101A (48-pin LQFP package)
+- IP101AH (48-pin LQFP package)
+
+Optional properties for the IP101GR (32-pin QFN package):
+
+- icplus,select-rx-error:
+  pin 21 ("RXER/INTR_32") will output the receive error status.
+  interrupts are not routed outside the PHY in this mode.
+- icplus,select-interrupt:
+  pin 21 ("RXER/INTR_32") will output the interrupt signal.
index 4b1a2a8..cc6b2c0 100644 (file)
@@ -170,6 +170,7 @@ holtek      Holtek Semiconductor, Inc.
 hwacom HwaCom Systems Inc.
 i2se   I2SE GmbH
 ibm    International Business Machines (IBM)
+icplus IC Plus Corp.
 idt    Integrated Device Technologies, Inc.
 ifi    Ingenieurburo Fur Ic-Technologie (I/F/I)
 ilitek ILI Technology Corporation (ILITEK)
@@ -1,4 +1,4 @@
-Documentation/networking/vortex.txt
+Documentation/networking/device_drivers/3com/vortex.txt
 Andrew Morton
 30 April 2000
 
@@ -19,8 +19,8 @@ pool management for network interfaces.
 This document provides an overview the Linux DPIO driver, its
 subcomponents, and its APIs.
 
-See Documentation/networking/dpaa2/overview.rst for a general overview of DPAA2
-and the general DPAA2 driver architecture in Linux.
+See Documentation/networking/device_drivers/freescale/dpaa2/overview.rst for
+a general overview of DPAA2 and the general DPAA2 driver architecture in Linux.
 
 Driver Overview
 ---------------
@@ -33,7 +33,7 @@ hardware resources, like queues, do not have a corresponding MC object and
 are treated as internal resources of other objects.
 
 For a more detailed description of the DPAA2 architecture and its object
-abstractions see *Documentation/networking/dpaa2/overview.rst*.
+abstractions see *Documentation/networking/device_drivers/freescale/dpaa2/overview.rst*.
 
 Each Linux net device is built on top of a Datapath Network Interface (DPNI)
 object and uses Buffer Pools (DPBPs), I/O Portals (DPIOs) and Concentrators
similarity index 97%
rename from Documentation/networking/ixgbe.rst
rename to Documentation/networking/device_drivers/intel/ixgbe.rst
index 725fc69..86d887a 100644 (file)
@@ -501,6 +501,19 @@ NOTE: This feature can be disabled for a specific Virtual Function (VF)::
 
   ip link set <pf dev> vf <vf id> spoofchk {off|on}
 
+IPsec Offload
+-------------
+The ixgbe driver supports IPsec Hardware Offload.  When creating Security
+Associations with "ip xfrm ..." the 'offload' tag option can be used to
+register the IPsec SA with the driver in order to get higher throughput in
+the secure communications.
+
+The offload is also supported for ixgbe's VFs, but the VF must be set as
+'trusted' and the support must be enabled with::
+
+  ethtool --set-priv-flags eth<x> vf-ipsec on
+  ip link set eth<x> vf <y> trust on
+
 
 Known Issues/Troubleshooting
 ============================
index ae444ff..2d26434 100644 (file)
@@ -40,3 +40,12 @@ msix_vec_per_pf_min  [DEVICE, GENERIC]
                        for the device initialization. Value is same across all
                        physical functions (PFs) in the device.
                        Type: u32
+
+fw_load_policy         [DEVICE, GENERIC]
+                       Controls the device's firmware loading policy.
+                       Valid values:
+                       * DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER (0)
+                         Load firmware version preferred by the driver.
+                       * DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH (1)
+                         Load firmware currently stored in flash.
+                       Type: u8
index bd89dae..6a47629 100644 (file)
@@ -31,6 +31,7 @@ Contents:
    net_failover
    alias
    bridge
+   snmp_counter
 
 .. only::  subproject
 
index 32b2157..acdfb5d 100644 (file)
@@ -108,8 +108,8 @@ neigh/default/gc_thresh2 - INTEGER
        Default: 512
 
 neigh/default/gc_thresh3 - INTEGER
-       Maximum number of neighbor entries allowed.  Increase this
-       when using large numbers of interfaces and when communicating
+       Maximum number of non-PERMANENT neighbor entries allowed.  Increase
+       this when using large numbers of interfaces and when communicating
        with large numbers of directly-connected peers.
        Default: 1024
 
@@ -370,6 +370,7 @@ tcp_l3mdev_accept - BOOLEAN
        derived from the listen socket to be bound to the L3 domain in
        which the packets originated. Only valid when the kernel was
        compiled with CONFIG_NET_L3_MASTER_DEV.
+        Default: 0 (disabled)
 
 tcp_low_latency - BOOLEAN
        This is a legacy option, it has no effect anymore.
@@ -758,7 +759,7 @@ tcp_limit_output_bytes - INTEGER
        flows, for typical pfifo_fast qdiscs.  tcp_limit_output_bytes
        limits the number of bytes on qdisc or device to reduce artificial
        RTT/cwnd and reduce bufferbloat.
-       Default: 262144
+       Default: 1048576 (16 * 65536)
 
 tcp_challenge_ack_limit - INTEGER
        Limits number of Challenge ACK sent per second, as recommended
@@ -773,6 +774,7 @@ udp_l3mdev_accept - BOOLEAN
        being received regardless of the L3 domain in which they
        originated. Only valid when the kernel was compiled with
        CONFIG_NET_L3_MASTER_DEV.
+        Default: 0 (disabled)
 
 udp_mem - vector of 3 INTEGERs: min, pressure, max
        Number of pages allowed for queueing by all UDP sockets.
@@ -799,6 +801,16 @@ udp_wmem_min - INTEGER
        total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
        Default: 4K
 
+RAW variables:
+
+raw_l3mdev_accept - BOOLEAN
+       Enabling this option allows a "global" bound socket to work
+       across L3 master domains (e.g., VRFs) with packets capable of
+       being received regardless of the L3 domain in which they
+       originated. Only valid when the kernel was compiled with
+       CONFIG_NET_L3_MASTER_DEV.
+       Default: 1 (enabled)
+
 CIPSOv4 Variables:
 
 cipso_cache_enable - BOOLEAN
index c4a54c1..58dd1c1 100644 (file)
@@ -115,7 +115,7 @@ set, be it TCPv4 (when NETIF_F_TSO is enabled) or TCPv6 (NETIF_F_TSO6).
 
  * Transmit UDP segmentation offload
 
-NETIF_F_GSO_UDP_GSO_L4 accepts a single UDP header with a payload that exceeds
+NETIF_F_GSO_UDP_L4 accepts a single UDP header with a payload that exceeds
 gso_size. On segmentation, it segments the payload on gso_size boundaries and
 replicates the network and UDP headers (fixing up the last one if less than
 gso_size).
diff --git a/Documentation/networking/snmp_counter.rst b/Documentation/networking/snmp_counter.rst
new file mode 100644 (file)
index 0000000..918a137
--- /dev/null
@@ -0,0 +1,947 @@
+===========
+SNMP counter
+===========
+
+This document explains the meaning of SNMP counters.
+
+General IPv4 counters
+====================
+All layer 4 packets and ICMP packets will change these counters, but
+these counters won't be changed by layer 2 packets (such as STP) or
+ARP packets.
+
+* IpInReceives
+Defined in `RFC1213 ipInReceives`_
+
+.. _RFC1213 ipInReceives: https://tools.ietf.org/html/rfc1213#page-26
+
+The number of packets received by the IP layer. It gets increasing at the
+beginning of ip_rcv function, always be updated together with
+IpExtInOctets. It indicates the number of aggregated segments after
+GRO/LRO.
+
+* IpInDelivers
+Defined in `RFC1213 ipInDelivers`_
+
+.. _RFC1213 ipInDelivers: https://tools.ietf.org/html/rfc1213#page-28
+
+The number of packets delivers to the upper layer protocols. E.g. TCP, UDP,
+ICMP and so on. If no one listens on a raw socket, only kernel
+supported protocols will be delivered, if someone listens on the raw
+socket, all valid IP packets will be delivered.
+
+* IpOutRequests
+Defined in `RFC1213 ipOutRequests`_
+
+.. _RFC1213 ipOutRequests: https://tools.ietf.org/html/rfc1213#page-28
+
+The number of packets sent via IP layer, for both single cast and
+multicast packets, and would always be updated together with
+IpExtOutOctets.
+
+* IpExtInOctets and IpExtOutOctets
+They are Linux kernel extensions, no RFC definitions. Please note,
+RFC1213 indeed defines ifInOctets  and ifOutOctets, but they
+are different things. The ifInOctets and ifOutOctets include the MAC
+layer header size but IpExtInOctets and IpExtOutOctets don't, they
+only include the IP layer header and the IP layer data.
+
+* IpExtInNoECTPkts, IpExtInECT1Pkts, IpExtInECT0Pkts, IpExtInCEPkts
+They indicate the number of four kinds of ECN IP packets, please refer
+`Explicit Congestion Notification`_ for more details.
+
+.. _Explicit Congestion Notification: https://tools.ietf.org/html/rfc3168#page-6
+
+These 4 counters calculate how many packets received per ECN
+status. They count the real frame number regardless the LRO/GRO. So
+for the same packet, you might find that IpInReceives count 1, but
+IpExtInNoECTPkts counts 2 or more.
+
+ICMP counters
+============
+* IcmpInMsgs and IcmpOutMsgs
+Defined by `RFC1213 icmpInMsgs`_ and `RFC1213 icmpOutMsgs`_
+
+.. _RFC1213 icmpInMsgs: https://tools.ietf.org/html/rfc1213#page-41
+.. _RFC1213 icmpOutMsgs: https://tools.ietf.org/html/rfc1213#page-43
+
+As mentioned in the RFC1213, these two counters include errors, they
+would be increased even if the ICMP packet has an invalid type. The
+ICMP output path will check the header of a raw socket, so the
+IcmpOutMsgs would still be updated if the IP header is constructed by
+a userspace program.
+
+* ICMP named types
+| These counters include most of common ICMP types, they are:
+| IcmpInDestUnreachs: `RFC1213 icmpInDestUnreachs`_
+| IcmpInTimeExcds: `RFC1213 icmpInTimeExcds`_
+| IcmpInParmProbs: `RFC1213 icmpInParmProbs`_
+| IcmpInSrcQuenchs: `RFC1213 icmpInSrcQuenchs`_
+| IcmpInRedirects: `RFC1213 icmpInRedirects`_
+| IcmpInEchos: `RFC1213 icmpInEchos`_
+| IcmpInEchoReps: `RFC1213 icmpInEchoReps`_
+| IcmpInTimestamps: `RFC1213 icmpInTimestamps`_
+| IcmpInTimestampReps: `RFC1213 icmpInTimestampReps`_
+| IcmpInAddrMasks: `RFC1213 icmpInAddrMasks`_
+| IcmpInAddrMaskReps: `RFC1213 icmpInAddrMaskReps`_
+| IcmpOutDestUnreachs: `RFC1213 icmpOutDestUnreachs`_
+| IcmpOutTimeExcds: `RFC1213 icmpOutTimeExcds`_
+| IcmpOutParmProbs: `RFC1213 icmpOutParmProbs`_
+| IcmpOutSrcQuenchs: `RFC1213 icmpOutSrcQuenchs`_
+| IcmpOutRedirects: `RFC1213 icmpOutRedirects`_
+| IcmpOutEchos: `RFC1213 icmpOutEchos`_
+| IcmpOutEchoReps: `RFC1213 icmpOutEchoReps`_
+| IcmpOutTimestamps: `RFC1213 icmpOutTimestamps`_
+| IcmpOutTimestampReps: `RFC1213 icmpOutTimestampReps`_
+| IcmpOutAddrMasks: `RFC1213 icmpOutAddrMasks`_
+| IcmpOutAddrMaskReps: `RFC1213 icmpOutAddrMaskReps`_
+
+.. _RFC1213 icmpInDestUnreachs: https://tools.ietf.org/html/rfc1213#page-41
+.. _RFC1213 icmpInTimeExcds: https://tools.ietf.org/html/rfc1213#page-41
+.. _RFC1213 icmpInParmProbs: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInRedirects: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInEchos: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInEchoReps: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInTimestamps: https://tools.ietf.org/html/rfc1213#page-42
+.. _RFC1213 icmpInTimestampReps: https://tools.ietf.org/html/rfc1213#page-43
+.. _RFC1213 icmpInAddrMasks: https://tools.ietf.org/html/rfc1213#page-43
+.. _RFC1213 icmpInAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-43
+
+.. _RFC1213 icmpOutDestUnreachs: https://tools.ietf.org/html/rfc1213#page-44
+.. _RFC1213 icmpOutTimeExcds: https://tools.ietf.org/html/rfc1213#page-44
+.. _RFC1213 icmpOutParmProbs: https://tools.ietf.org/html/rfc1213#page-44
+.. _RFC1213 icmpOutSrcQuenchs: https://tools.ietf.org/html/rfc1213#page-44
+.. _RFC1213 icmpOutRedirects: https://tools.ietf.org/html/rfc1213#page-44
+.. _RFC1213 icmpOutEchos: https://tools.ietf.org/html/rfc1213#page-45
+.. _RFC1213 icmpOutEchoReps: https://tools.ietf.org/html/rfc1213#page-45
+.. _RFC1213 icmpOutTimestamps: https://tools.ietf.org/html/rfc1213#page-45
+.. _RFC1213 icmpOutTimestampReps: https://tools.ietf.org/html/rfc1213#page-45
+.. _RFC1213 icmpOutAddrMasks: https://tools.ietf.org/html/rfc1213#page-45
+.. _RFC1213 icmpOutAddrMaskReps: https://tools.ietf.org/html/rfc1213#page-46
+
+Every ICMP type has two counters: 'In' and 'Out'. E.g., for the ICMP
+Echo packet, they are IcmpInEchos and IcmpOutEchos. Their meanings are
+straightforward. The 'In' counter means kernel receives such a packet
+and the 'Out' counter means kernel sends such a packet.
+
+* ICMP numeric types
+They are IcmpMsgInType[N] and IcmpMsgOutType[N], the [N] indicates the
+ICMP type number. These counters track all kinds of ICMP packets. The
+ICMP type number definition could be found in the `ICMP parameters`_
+document.
+
+.. _ICMP parameters: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
+
+For example, if the Linux kernel sends an ICMP Echo packet, the
+IcmpMsgOutType8 would increase 1. And if kernel gets an ICMP Echo Reply
+packet, IcmpMsgInType0 would increase 1.
+
+* IcmpInCsumErrors
+This counter indicates the checksum of the ICMP packet is
+wrong. Kernel verifies the checksum after updating the IcmpInMsgs and
+before updating IcmpMsgInType[N]. If a packet has bad checksum, the
+IcmpInMsgs would be updated but none of IcmpMsgInType[N] would be updated.
+
+* IcmpInErrors and IcmpOutErrors
+Defined by `RFC1213 icmpInErrors`_ and `RFC1213 icmpOutErrors`_
+
+.. _RFC1213 icmpInErrors: https://tools.ietf.org/html/rfc1213#page-41
+.. _RFC1213 icmpOutErrors: https://tools.ietf.org/html/rfc1213#page-43
+
+When an error occurs in the ICMP packet handler path, these two
+counters would be updated. The receiving packet path use IcmpInErrors
+and the sending packet path use IcmpOutErrors. When IcmpInCsumErrors
+is increased, IcmpInErrors would always be increased too.
+
+relationship of the ICMP counters
+-------------------------------
+The sum of IcmpMsgOutType[N] is always equal to IcmpOutMsgs, as they
+are updated at the same time. The sum of IcmpMsgInType[N] plus
+IcmpInErrors should be equal or larger than IcmpInMsgs. When kernel
+receives an ICMP packet, kernel follows below logic:
+
+1. increase IcmpInMsgs
+2. if has any error, update IcmpInErrors and finish the process
+3. update IcmpMsgOutType[N]
+4. handle the packet depending on the type, if has any error, update
+   IcmpInErrors and finish the process
+
+So if all errors occur in step (2), IcmpInMsgs should be equal to the
+sum of IcmpMsgOutType[N] plus IcmpInErrors. If all errors occur in
+step (4), IcmpInMsgs should be equal to the sum of
+IcmpMsgOutType[N]. If the errors occur in both step (2) and step (4),
+IcmpInMsgs should be less than the sum of IcmpMsgOutType[N] plus
+IcmpInErrors.
+
+General TCP counters
+==================
+* TcpInSegs
+Defined in `RFC1213 tcpInSegs`_
+
+.. _RFC1213 tcpInSegs: https://tools.ietf.org/html/rfc1213#page-48
+
+The number of packets received by the TCP layer. As mentioned in
+RFC1213, it includes the packets received in error, such as checksum
+error, invalid TCP header and so on. Only one error won't be included:
+if the layer 2 destination address is not the NIC's layer 2
+address. It might happen if the packet is a multicast or broadcast
+packet, or the NIC is in promiscuous mode. In these situations, the
+packets would be delivered to the TCP layer, but the TCP layer will discard
+these packets before increasing TcpInSegs. The TcpInSegs counter
+isn't aware of GRO. So if two packets are merged by GRO, the TcpInSegs
+counter would only increase 1.
+
+* TcpOutSegs
+Defined in `RFC1213 tcpOutSegs`_
+
+.. _RFC1213 tcpOutSegs: https://tools.ietf.org/html/rfc1213#page-48
+
+The number of packets sent by the TCP layer. As mentioned in RFC1213,
+it excludes the retransmitted packets. But it includes the SYN, ACK
+and RST packets. Doesn't like TcpInSegs, the TcpOutSegs is aware of
+GSO, so if a packet would be split to 2 by GSO, TcpOutSegs will
+increase 2.
+
+* TcpActiveOpens
+Defined in `RFC1213 tcpActiveOpens`_
+
+.. _RFC1213 tcpActiveOpens: https://tools.ietf.org/html/rfc1213#page-47
+
+It means the TCP layer sends a SYN, and come into the SYN-SENT
+state. Every time TcpActiveOpens increases 1, TcpOutSegs should always
+increase 1.
+
+* TcpPassiveOpens
+Defined in `RFC1213 tcpPassiveOpens`_
+
+.. _RFC1213 tcpPassiveOpens: https://tools.ietf.org/html/rfc1213#page-47
+
+It means the TCP layer receives a SYN, replies a SYN+ACK, come into
+the SYN-RCVD state.
+
+* TcpExtTCPRcvCoalesce
+When packets are received by the TCP layer and are not be read by the
+application, the TCP layer will try to merge them. This counter
+indicate how many packets are merged in such situation. If GRO is
+enabled, lots of packets would be merged by GRO, these packets
+wouldn't be counted to TcpExtTCPRcvCoalesce.
+
+* TcpExtTCPAutoCorking
+When sending packets, the TCP layer will try to merge small packets to
+a bigger one. This counter increase 1 for every packet merged in such
+situation. Please refer to the LWN article for more details:
+https://lwn.net/Articles/576263/
+
+* TcpExtTCPOrigDataSent
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPOrigDataSent: number of outgoing packets with original data (excluding
+  retransmission but including data-in-SYN). This counter is different from
+  TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is
+  more useful to track the TCP retransmission rate.
+
+* TCPSynRetrans
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down
+  retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
+
+* TCPFastOpenActiveFail
+This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
+explaination below::
+
+  TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because
+  the remote does not accept it or the attempts timed out.
+
+.. _kernel commit f19c29e3e391: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f19c29e3e391a66a273e9afebaf01917245148cd
+
+* TcpExtListenOverflows and TcpExtListenDrops
+When kernel receives a SYN from a client, and if the TCP accept queue
+is full, kernel will drop the SYN and add 1 to TcpExtListenOverflows.
+At the same time kernel will also add 1 to TcpExtListenDrops. When a
+TCP socket is in LISTEN state, and kernel need to drop a packet,
+kernel would always add 1 to TcpExtListenDrops. So increase
+TcpExtListenOverflows would let TcpExtListenDrops increasing at the
+same time, but TcpExtListenDrops would also increase without
+TcpExtListenOverflows increasing, e.g. a memory allocation fail would
+also let TcpExtListenDrops increase.
+
+Note: The above explanation is based on kernel 4.10 or above version, on
+an old kernel, the TCP stack has different behavior when TCP accept
+queue is full. On the old kernel, TCP stack won't drop the SYN, it
+would complete the 3-way handshake. As the accept queue is full, TCP
+stack will keep the socket in the TCP half-open queue. As it is in the
+half open queue, TCP stack will send SYN+ACK on an exponential backoff
+timer, after client replies ACK, TCP stack checks whether the accept
+queue is still full, if it is not full, moves the socket to the accept
+queue, if it is full, keeps the socket in the half-open queue, at next
+time client replies ACK, this socket will get another chance to move
+to the accept queue.
+
+
+TCP Fast Open
+============
+When kernel receives a TCP packet, it has two paths to handler the
+packet, one is fast path, another is slow path. The comment in kernel
+code provides a good explanation of them, I pasted them below::
+
+  It is split into a fast path and a slow path. The fast path is
+  disabled when:
+
+  - A zero window was announced from us
+  - zero window probing
+    is only handled properly on the slow path.
+  - Out of order segments arrived.
+  - Urgent data is expected.
+  - There is no buffer space left
+  - Unexpected TCP flags/window values/header lengths are received
+    (detected by checking the TCP header against pred_flags)
+  - Data is sent in both directions. The fast path only supports pure senders
+    or pure receivers (this means either the sequence number or the ack
+    value must stay constant)
+  - Unexpected TCP option.
+
+Kernel will try to use fast path unless any of the above conditions
+are satisfied. If the packets are out of order, kernel will handle
+them in slow path, which means the performance might be not very
+good. Kernel would also come into slow path if the "Delayed ack" is
+used, because when using "Delayed ack", the data is sent in both
+directions. When the TCP window scale option is not used, kernel will
+try to enable fast path immediately when the connection comes into the
+established state, but if the TCP window scale option is used, kernel
+will disable the fast path at first, and try to enable it after kernel
+receives packets.
+
+* TcpExtTCPPureAcks and TcpExtTCPHPAcks
+If a packet set ACK flag and has no data, it is a pure ACK packet, if
+kernel handles it in the fast path, TcpExtTCPHPAcks will increase 1,
+if kernel handles it in the slow path, TcpExtTCPPureAcks will
+increase 1.
+
+* TcpExtTCPHPHits
+If a TCP packet has data (which means it is not a pure ACK packet),
+and this packet is handled in the fast path, TcpExtTCPHPHits will
+increase 1.
+
+
+TCP abort
+========
+
+
+* TcpExtTCPAbortOnData
+It means TCP layer has data in flight, but need to close the
+connection. So TCP layer sends a RST to the other side, indicate the
+connection is not closed very graceful. An easy way to increase this
+counter is using the SO_LINGER option. Please refer to the SO_LINGER
+section of the `socket man page`_:
+
+.. _socket man page: http://man7.org/linux/man-pages/man7/socket.7.html
+
+By default, when an application closes a connection, the close function
+will return immediately and kernel will try to send the in-flight data
+async. If you use the SO_LINGER option, set l_onoff to 1, and l_linger
+to a positive number, the close function won't return immediately, but
+wait for the in-flight data are acked by the other side, the max wait
+time is l_linger seconds. If set l_onoff to 1 and set l_linger to 0,
+when the application closes a connection, kernel will send a RST
+immediately and increase the TcpExtTCPAbortOnData counter.
+
+* TcpExtTCPAbortOnClose
+This counter means the application has unread data in the TCP layer when
+the application wants to close the TCP connection. In such a situation,
+kernel will send a RST to the other side of the TCP connection.
+
+* TcpExtTCPAbortOnMemory
+When an application closes a TCP connection, kernel still need to track
+the connection, let it complete the TCP disconnect process. E.g. an
+app calls the close method of a socket, kernel sends fin to the other
+side of the connection, then the app has no relationship with the
+socket any more, but kernel need to keep the socket, this socket
+becomes an orphan socket, kernel waits for the reply of the other side,
+and would come to the TIME_WAIT state finally. When kernel has no
+enough memory to keep the orphan socket, kernel would send an RST to
+the other side, and delete the socket, in such situation, kernel will
+increase 1 to the TcpExtTCPAbortOnMemory. Two conditions would trigger
+TcpExtTCPAbortOnMemory:
+
+1. the memory used by the TCP protocol is higher than the third value of
+the tcp_mem. Please refer the tcp_mem section in the `TCP man page`_:
+
+.. _TCP man page: http://man7.org/linux/man-pages/man7/tcp.7.html
+
+2. the orphan socket count is higher than net.ipv4.tcp_max_orphans
+
+
+* TcpExtTCPAbortOnTimeout
+This counter will increase when any of the TCP timers expire. In such
+situation, kernel won't send RST, just give up the connection.
+
+* TcpExtTCPAbortOnLinger
+When a TCP connection comes into FIN_WAIT_2 state, instead of waiting
+for the fin packet from the other side, kernel could send a RST and
+delete the socket immediately. This is not the default behavior of
+Linux kernel TCP stack. By configuring the TCP_LINGER2 socket option,
+you could let kernel follow this behavior.
+
+* TcpExtTCPAbortFailed
+The kernel TCP layer will send RST if the `RFC2525 2.17 section`_ is
+satisfied. If an internal error occurs during this process,
+TcpExtTCPAbortFailed will be increased.
+
+.. _RFC2525 2.17 section: https://tools.ietf.org/html/rfc2525#page-50
+
+TCP Hybrid Slow Start
+====================
+The Hybrid Slow Start algorithm is an enhancement of the traditional
+TCP congestion window Slow Start algorithm. It uses two pieces of
+information to detect whether the max bandwidth of the TCP path is
+approached. The two pieces of information are ACK train length and
+increase in packet delay. For detail information, please refer the
+`Hybrid Slow Start paper`_. Either ACK train length or packet delay
+hits a specific threshold, the congestion control algorithm will come
+into the Congestion Avoidance state. Until v4.20, two congestion
+control algorithms are using Hybrid Slow Start, they are cubic (the
+default congestion control algorithm) and cdg. Four snmp counters
+relate with the Hybrid Slow Start algorithm.
+
+.. _Hybrid Slow Start paper: https://pdfs.semanticscholar.org/25e9/ef3f03315782c7f1cbcd31b587857adae7d1.pdf
+
+* TcpExtTCPHystartTrainDetect
+How many times the ACK train length threshold is detected
+
+* TcpExtTCPHystartTrainCwnd
+The sum of CWND detected by ACK train length. Dividing this value by
+TcpExtTCPHystartTrainDetect is the average CWND which detected by the
+ACK train length.
+
+* TcpExtTCPHystartDelayDetect
+How many times the packet delay threshold is detected.
+
+* TcpExtTCPHystartDelayCwnd
+The sum of CWND detected by packet delay. Dividing this value by
+TcpExtTCPHystartDelayDetect is the average CWND which detected by the
+packet delay.
+
+examples
+=======
+
+ping test
+--------
+Run the ping command against the public dns server 8.8.8.8::
+
+  nstatuser@nstat-a:~$ ping 8.8.8.8 -c 1
+  PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
+  64 bytes from 8.8.8.8: icmp_seq=1 ttl=119 time=17.8 ms
+
+  --- 8.8.8.8 ping statistics ---
+  1 packets transmitted, 1 received, 0% packet loss, time 0ms
+  rtt min/avg/max/mdev = 17.875/17.875/17.875/0.000 ms
+
+The nstayt result::
+
+  nstatuser@nstat-a:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  IcmpInMsgs                      1                  0.0
+  IcmpInEchoReps                  1                  0.0
+  IcmpOutMsgs                     1                  0.0
+  IcmpOutEchos                    1                  0.0
+  IcmpMsgInType0                  1                  0.0
+  IcmpMsgOutType8                 1                  0.0
+  IpExtInOctets                   84                 0.0
+  IpExtOutOctets                  84                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+The Linux server sent an ICMP Echo packet, so IpOutRequests,
+IcmpOutMsgs, IcmpOutEchos and IcmpMsgOutType8 were increased 1. The
+server got ICMP Echo Reply from 8.8.8.8, so IpInReceives, IcmpInMsgs,
+IcmpInEchoReps and IcmpMsgInType0 were increased 1. The ICMP Echo Reply
+was passed to the ICMP layer via IP layer, so IpInDelivers was
+increased 1. The default ping data size is 48, so an ICMP Echo packet
+and its corresponding Echo Reply packet are constructed by:
+
+* 14 bytes MAC header
+* 20 bytes IP header
+* 16 bytes ICMP header
+* 48 bytes data (default value of the ping command)
+
+So the IpExtInOctets and IpExtOutOctets are 20+16+48=84.
+
+tcp 3-way handshake
+------------------
+On server side, we run::
+
+  nstatuser@nstat-b:~$ nc -lknv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+On client side, we run::
+
+  nstatuser@nstat-a:~$ nc -nv 192.168.122.251 9000
+  Connection to 192.168.122.251 9000 port [tcp/*] succeeded!
+
+The server listened on tcp 9000 port, the client connected to it, they
+completed the 3-way handshake.
+
+On server side, we can find below nstat output::
+
+  nstatuser@nstat-b:~$ nstat | grep -i tcp
+  TcpPassiveOpens                 1                  0.0
+  TcpInSegs                       2                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPPureAcks               1                  0.0
+
+On client side, we can find below nstat output::
+
+  nstatuser@nstat-a:~$ nstat | grep -i tcp
+  TcpActiveOpens                  1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      2                  0.0
+
+When the server received the first SYN, it replied a SYN+ACK, and came into
+SYN-RCVD state, so TcpPassiveOpens increased 1. The server received
+SYN, sent SYN+ACK, received ACK, so server sent 1 packet, received 2
+packets, TcpInSegs increased 2, TcpOutSegs increased 1. The last ACK
+of the 3-way handshake is a pure ACK without data, so
+TcpExtTCPPureAcks increased 1.
+
+When the client sent SYN, the client came into the SYN-SENT state, so
+TcpActiveOpens increased 1, the client sent SYN, received SYN+ACK, sent
+ACK, so client sent 2 packets, received 1 packet, TcpInSegs increased
+1, TcpOutSegs increased 2.
+
+TCP normal traffic
+-----------------
+Run nc on server::
+
+  nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+Run nc on client::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+
+Input a string in the nc client ('hello' in our example)::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+  hello
+
+The client side nstat output::
+
+  nstatuser@nstat-a:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPPureAcks               1                  0.0
+  TcpExtTCPOrigDataSent           1                  0.0
+  IpExtInOctets                   52                 0.0
+  IpExtOutOctets                  58                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+The server side nstat output::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  IpExtInOctets                   58                 0.0
+  IpExtOutOctets                  52                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+Input a string in nc client side again ('world' in our exmaple)::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+  hello
+  world
+
+Client side nstat output::
+
+  nstatuser@nstat-a:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPHPAcks                 1                  0.0
+  TcpExtTCPOrigDataSent           1                  0.0
+  IpExtInOctets                   52                 0.0
+  IpExtOutOctets                  58                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+
+Server side nstat output::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    1                  0.0
+  IpInDelivers                    1                  0.0
+  IpOutRequests                   1                  0.0
+  TcpInSegs                       1                  0.0
+  TcpOutSegs                      1                  0.0
+  TcpExtTCPHPHits                 1                  0.0
+  IpExtInOctets                   58                 0.0
+  IpExtOutOctets                  52                 0.0
+  IpExtInNoECTPkts                1                  0.0
+
+Compare the first client-side nstat and the second client-side nstat,
+we could find one difference: the first one had a 'TcpExtTCPPureAcks',
+but the second one had a 'TcpExtTCPHPAcks'. The first server-side
+nstat and the second server-side nstat had a difference too: the
+second server-side nstat had a TcpExtTCPHPHits, but the first
+server-side nstat didn't have it. The network traffic patterns were
+exactly the same: the client sent a packet to the server, the server
+replied an ACK. But kernel handled them in different ways. When the
+TCP window scale option is not used, kernel will try to enable fast
+path immediately when the connection comes into the established state,
+but if the TCP window scale option is used, kernel will disable the
+fast path at first, and try to enable it after kerenl receives
+packets. We could use the 'ss' command to verify whether the window
+scale option is used. e.g. run below command on either server or
+client::
+
+  nstatuser@nstat-a:~$ ss -o state established -i '( dport = :9000 or sport = :9000 )
+  Netid    Recv-Q     Send-Q            Local Address:Port             Peer Address:Port
+  tcp      0          0               192.168.122.250:40654         192.168.122.251:9000
+             ts sack cubic wscale:7,7 rto:204 rtt:0.98/0.49 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:10 bytes_acked:1 segs_out:2 segs_in:1 send 118.2Mbps lastsnd:46572 lastrcv:46572 lastack:46572 pacing_rate 236.4Mbps rcv_space:29200 rcv_ssthresh:29200 minrtt:0.98
+
+The 'wscale:7,7' means both server and client set the window scale
+option to 7. Now we could explain the nstat output in our test:
+
+In the first nstat output of client side, the client sent a packet, server
+reply an ACK, when kernel handled this ACK, the fast path was not
+enabled, so the ACK was counted into 'TcpExtTCPPureAcks'.
+
+In the second nstat output of client side, the client sent a packet again,
+and received another ACK from the server, in this time, the fast path is
+enabled, and the ACK was qualified for fast path, so it was handled by
+the fast path, so this ACK was counted into TcpExtTCPHPAcks.
+
+In the first nstat output of server side, fast path was not enabled,
+so there was no 'TcpExtTCPHPHits'.
+
+In the second nstat output of server side, the fast path was enabled,
+and the packet received from client qualified for fast path, so it
+was counted into 'TcpExtTCPHPHits'.
+
+TcpExtTCPAbortOnClose
+--------------------
+On the server side, we run below python script::
+
+  import socket
+  import time
+
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+This python script listen on 9000 port, but doesn't read anything from
+the connection.
+
+On the client side, we send the string "hello" by nc::
+
+  nstatuser@nstat-a:~$ echo "hello" | nc nstat-b 9000
+
+Then, we come back to the server side, the server has received the "hello"
+packet, and the TCP layer has acked this packet, but the application didn't
+read it yet. We type Ctrl-C to terminate the server script. Then we
+could find TcpExtTCPAbortOnClose increased 1 on the server side::
+
+  nstatuser@nstat-b:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnClose           1                  0.0
+
+If we run tcpdump on the server side, we could find the server sent a
+RST after we type Ctrl-C.
+
+TcpExtTCPAbortOnMemory and TcpExtTCPAbortOnTimeout
+-----------------------------------------------
+Below is an example which let the orphan socket count be higher than
+net.ipv4.tcp_max_orphans.
+Change tcp_max_orphans to a smaller value on client::
+
+  sudo bash -c "echo 10 > /proc/sys/net/ipv4/tcp_max_orphans"
+
+Client code (create 64 connection to server)::
+
+  nstatuser@nstat-a:~$ cat client_orphan.py
+  import socket
+  import time
+
+  server = 'nstat-b' # server address
+  port = 9000
+
+  count = 64
+
+  connection_list = []
+
+  for i in range(64):
+      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      s.connect((server, port))
+      connection_list.append(s)
+      print("connection_count: %d" % len(connection_list))
+
+  while True:
+      time.sleep(99999)
+
+Server code (accept 64 connection from client)::
+
+  nstatuser@nstat-b:~$ cat server_orphan.py
+  import socket
+  import time
+
+  port = 9000
+  count = 64
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(count)
+  connection_list = []
+  while True:
+      sock, addr = s.accept()
+      connection_list.append((sock, addr))
+      print("connection_count: %d" % len(connection_list))
+
+Run the python scripts on server and client.
+
+On server::
+
+  python3 server_orphan.py
+
+On client::
+
+  python3 client_orphan.py
+
+Run iptables on server::
+
+  sudo iptables -A INPUT -i ens3 -p tcp --destination-port 9000 -j DROP
+
+Type Ctrl-C on client, stop client_orphan.py.
+
+Check TcpExtTCPAbortOnMemory on client::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnMemory          54                 0.0
+
+Check orphane socket count on client::
+
+  nstatuser@nstat-a:~$ ss -s
+  Total: 131 (kernel 0)
+  TCP:   14 (estab 1, closed 0, orphaned 10, synrecv 0, timewait 0/0), ports 0
+
+  Transport Total     IP        IPv6
+  *         0         -         -
+  RAW       1         0         1
+  UDP       1         1         0
+  TCP       14        13        1
+  INET      16        14        2
+  FRAG      0         0         0
+
+The explanation of the test: after run server_orphan.py and
+client_orphan.py, we set up 64 connections between server and
+client. Run the iptables command, the server will drop all packets from
+the client, type Ctrl-C on client_orphan.py, the system of the client
+would try to close these connections, and before they are closed
+gracefully, these connections became orphan sockets. As the iptables
+of the server blocked packets from the client, the server won't receive fin
+from the client, so all connection on clients would be stuck on FIN_WAIT_1
+stage, so they will keep as orphan sockets until timeout. We have echo
+10 to /proc/sys/net/ipv4/tcp_max_orphans, so the client system would
+only keep 10 orphan sockets, for all other orphan sockets, the client
+system sent RST for them and delete them. We have 64 connections, so
+the 'ss -s' command shows the system has 10 orphan sockets, and the
+value of TcpExtTCPAbortOnMemory was 54.
+
+An additional explanation about orphan socket count: You could find the
+exactly orphan socket count by the 'ss -s' command, but when kernel
+decide whither increases TcpExtTCPAbortOnMemory and sends RST, kernel
+doesn't always check the exactly orphan socket count. For increasing
+performance, kernel checks an approximate count firstly, if the
+approximate count is more than tcp_max_orphans, kernel checks the
+exact count again. So if the approximate count is less than
+tcp_max_orphans, but exactly count is more than tcp_max_orphans, you
+would find TcpExtTCPAbortOnMemory is not increased at all. If
+tcp_max_orphans is large enough, it won't occur, but if you decrease
+tcp_max_orphans to a small value like our test, you might find this
+issue. So in our test, the client set up 64 connections although the
+tcp_max_orphans is 10. If the client only set up 11 connections, we
+can't find the change of TcpExtTCPAbortOnMemory.
+
+Continue the previous test, we wait for several minutes. Because of the
+iptables on the server blocked the traffic, the server wouldn't receive
+fin, and all the client's orphan sockets would timeout on the
+FIN_WAIT_1 state finally. So we wait for a few minutes, we could find
+10 timeout on the client::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnTimeout         10                 0.0
+
+TcpExtTCPAbortOnLinger
+---------------------
+The server side code::
+
+  nstatuser@nstat-b:~$ cat server_linger.py
+  import socket
+  import time
+
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+The client side code::
+
+  nstatuser@nstat-a:~$ cat client_linger.py
+  import socket
+  import struct
+
+  server = 'nstat-b' # server address
+  port = 9000
+
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 10))
+  s.setsockopt(socket.SOL_TCP, socket.TCP_LINGER2, struct.pack('i', -1))
+  s.connect((server, port))
+  s.close()
+
+Run server_linger.py on server::
+
+  nstatuser@nstat-b:~$ python3 server_linger.py
+
+Run client_linger.py on client::
+
+  nstatuser@nstat-a:~$ python3 client_linger.py
+
+After run client_linger.py, check the output of nstat::
+
+  nstatuser@nstat-a:~$ nstat | grep -i abort
+  TcpExtTCPAbortOnLinger          1                  0.0
+
+TcpExtTCPRcvCoalesce
+-------------------
+On the server, we run a program which listen on TCP port 9000, but
+doesn't read any data::
+
+  import socket
+  import time
+  port = 9000
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.bind(('0.0.0.0', port))
+  s.listen(1)
+  sock, addr = s.accept()
+  while True:
+      time.sleep(9999999)
+
+Save the above code as server_coalesce.py, and run::
+
+  python3 server_coalesce.py
+
+On the client, save below code as client_coalesce.py::
+
+  import socket
+  server = 'nstat-b'
+  port = 9000
+  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+  s.connect((server, port))
+
+Run::
+
+  nstatuser@nstat-a:~$ python3 -i client_coalesce.py
+
+We use '-i' to come into the interactive mode, then a packet::
+
+  >>> s.send(b'foo')
+  3
+
+Send a packet again::
+
+  >>> s.send(b'bar')
+  3
+
+On the server, run nstat::
+
+  ubuntu@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    2                  0.0
+  IpInDelivers                    2                  0.0
+  IpOutRequests                   2                  0.0
+  TcpInSegs                       2                  0.0
+  TcpOutSegs                      2                  0.0
+  TcpExtTCPRcvCoalesce            1                  0.0
+  IpExtInOctets                   110                0.0
+  IpExtOutOctets                  104                0.0
+  IpExtInNoECTPkts                2                  0.0
+
+The client sent two packets, server didn't read any data. When
+the second packet arrived at server, the first packet was still in
+the receiving queue. So the TCP layer merged the two packets, and we
+could find the TcpExtTCPRcvCoalesce increased 1.
+
+TcpExtListenOverflows and TcpExtListenDrops
+----------------------------------------
+On server, run the nc command, listen on port 9000::
+
+  nstatuser@nstat-b:~$ nc -lkv 0.0.0.0 9000
+  Listening on [0.0.0.0] (family 0, port 9000)
+
+On client, run 3 nc commands in different terminals::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+  Connection to nstat-b 9000 port [tcp/*] succeeded!
+
+The nc command only accepts 1 connection, and the accept queue length
+is 1. On current linux implementation, set queue length to n means the
+actual queue length is n+1. Now we create 3 connections, 1 is accepted
+by nc, 2 in accepted queue, so the accept queue is full.
+
+Before running the 4th nc, we clean the nstat history on the server::
+
+  nstatuser@nstat-b:~$ nstat -n
+
+Run the 4th nc on the client::
+
+  nstatuser@nstat-a:~$ nc -v nstat-b 9000
+
+If the nc server is running on kernel 4.10 or higher version, you
+won't see the "Connection to ... succeeded!" string, because kernel
+will drop the SYN if the accept queue is full. If the nc client is running
+on an old kernel, you would see that the connection is succeeded,
+because kernel would complete the 3 way handshake and keep the socket
+on half open queue. I did the test on kernel 4.15. Below is the nstat
+on the server::
+
+  nstatuser@nstat-b:~$ nstat
+  #kernel
+  IpInReceives                    4                  0.0
+  IpInDelivers                    4                  0.0
+  TcpInSegs                       4                  0.0
+  TcpExtListenOverflows           4                  0.0
+  TcpExtListenDrops               4                  0.0
+  IpExtInOctets                   240                0.0
+  IpExtInNoECTPkts                4                  0.0
+
+Both TcpExtListenOverflows and TcpExtListenDrops were 4. If the time
+between the 4th nc and the nstat was longer, the value of
+TcpExtListenOverflows and TcpExtListenDrops would be larger, because
+the SYN of the 4th nc was dropped, the client was retrying.
index 8ff7b4c..a5f103b 100644 (file)
@@ -103,19 +103,33 @@ VRF device:
 
 or to specify the output device using cmsg and IP_PKTINFO.
 
+By default the scope of the port bindings for unbound sockets is
+limited to the default VRF. That is, it will not be matched by packets
+arriving on interfaces enslaved to an l3mdev and processes may bind to
+the same port if they bind to an l3mdev.
+
 TCP & UDP services running in the default VRF context (ie., not bound
 to any VRF device) can work across all VRF domains by enabling the
 tcp_l3mdev_accept and udp_l3mdev_accept sysctl options:
+
     sysctl -w net.ipv4.tcp_l3mdev_accept=1
     sysctl -w net.ipv4.udp_l3mdev_accept=1
 
+These options are disabled by default so that a socket in a VRF is only
+selected for packets in that VRF. There is a similar option for RAW
+sockets, which is enabled by default for reasons of backwards compatibility.
+This is so as to specify the output device with cmsg and IP_PKTINFO, but
+using a socket not bound to the corresponding VRF. This allows e.g. older ping
+implementations to be run with specifying the device but without executing it
+in the VRF. This option can be disabled so that packets received in a VRF
+context are only handled by a raw socket bound to the VRF, and packets in the
+default VRF are only handled by a socket not bound to any VRF:
+
+    sysctl -w net.ipv4.raw_l3mdev_accept=0
+
 netfilter rules on the VRF device can be used to limit access to services
 running in the default VRF context as well.
 
-The default VRF does not have limited scope with respect to port bindings.
-That is, if a process does a wildcard bind to a port in the default VRF it
-owns the port across all VRF domains within the network namespace.
-
 ################################################################################
 
 Using iproute2 for VRFs
index 8119141..a676b40 100644 (file)
@@ -140,7 +140,7 @@ Maintainers List (try to look for most precise areas first)
 M:     Steffen Klassert <klassert@kernel.org>
 L:     netdev@vger.kernel.org
 S:     Odd Fixes
-F:     Documentation/networking/vortex.txt
+F:     Documentation/networking/device_drivers/3com/vortex.txt
 F:     drivers/net/ethernet/3com/3c59x.c
 
 3CR990 NETWORK DRIVER
@@ -740,7 +740,7 @@ R:  Saeed Bishara <saeedb@amazon.com>
 R:     Zorik Machulsky <zorik@amazon.com>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/ena.txt
+F:     Documentation/networking/device_drivers/amazon/ena.txt
 F:     drivers/net/ethernet/amazon/
 
 AMD CRYPTOGRAPHIC COPROCESSOR (CCP) DRIVER
@@ -4190,7 +4190,7 @@ F:        net/ax25/sysctl_net_ax25.c
 DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
 L:     netdev@vger.kernel.org
 S:     Orphan
-F:     Documentation/networking/dmfe.txt
+F:     Documentation/networking/device_drivers/dec/dmfe.txt
 F:     drivers/net/ethernet/dec/tulip/dmfe.c
 
 DC390/AM53C974 SCSI driver
@@ -5613,6 +5613,7 @@ F:        include/linux/of_net.h
 F:     include/linux/phy.h
 F:     include/linux/phy_fixed.h
 F:     include/linux/platform_data/mdio-bcm-unimac.h
+F:     include/linux/platform_data/mdio-gpio.h
 F:     include/trace/events/mdio.h
 F:     include/uapi/linux/mdio.h
 F:     include/uapi/linux/mii.h
@@ -6903,7 +6904,7 @@ M:        Haiyang Zhang <haiyangz@microsoft.com>
 M:     Stephen Hemminger <sthemmin@microsoft.com>
 L:     devel@linuxdriverproject.org
 S:     Maintained
-F:     Documentation/networking/netvsc.txt
+F:     Documentation/networking/device_drivers/microsoft/netvsc.txt
 F:     arch/x86/include/asm/mshyperv.h
 F:     arch/x86/include/asm/trace/hyperv.h
 F:     arch/x86/include/asm/hyperv-tlfs.h
@@ -7487,18 +7488,18 @@ Q:      http://patchwork.ozlabs.org/project/intel-wired-lan/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
 S:     Supported
-F:     Documentation/networking/e100.rst
-F:     Documentation/networking/e1000.rst
-F:     Documentation/networking/e1000e.rst
-F:     Documentation/networking/fm10k.rst
-F:     Documentation/networking/igb.rst
-F:     Documentation/networking/igbvf.rst
-F:     Documentation/networking/ixgb.rst
-F:     Documentation/networking/ixgbe.rst
-F:     Documentation/networking/ixgbevf.rst
-F:     Documentation/networking/i40e.rst
-F:     Documentation/networking/iavf.rst
-F:     Documentation/networking/ice.rst
+F:     Documentation/networking/device_drivers/intel/e100.rst
+F:     Documentation/networking/device_drivers/intel/e1000.rst
+F:     Documentation/networking/device_drivers/intel/e1000e.rst
+F:     Documentation/networking/device_drivers/intel/fm10k.rst
+F:     Documentation/networking/device_drivers/intel/igb.rst
+F:     Documentation/networking/device_drivers/intel/igbvf.rst
+F:     Documentation/networking/device_drivers/intel/ixgb.rst
+F:     Documentation/networking/device_drivers/intel/ixgbe.rst
+F:     Documentation/networking/device_drivers/intel/ixgbevf.rst
+F:     Documentation/networking/device_drivers/intel/i40e.rst
+F:     Documentation/networking/device_drivers/intel/iavf.rst
+F:     Documentation/networking/device_drivers/intel/ice.rst
 F:     drivers/net/ethernet/intel/
 F:     drivers/net/ethernet/intel/*/
 F:     include/linux/avf/virtchnl.h
@@ -7672,8 +7673,8 @@ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
 M:     Stanislav Yakovlev <stas.yakovlev@gmail.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-F:     Documentation/networking/README.ipw2100
-F:     Documentation/networking/README.ipw2200
+F:     Documentation/networking/device_drivers/intel/ipw2100.txt
+F:     Documentation/networking/device_drivers/intel/ipw2200.txt
 F:     drivers/net/wireless/intel/ipw2x00/
 
 INTEL PSTATE DRIVER
@@ -7933,13 +7934,6 @@ F:       include/linux/isdn/
 F:     include/uapi/linux/isdn.h
 F:     include/uapi/linux/isdn/
 
-ISDN SUBSYSTEM (Eicon active card driver)
-M:     Armin Schindler <mac@melware.de>
-L:     isdn4linux@listserv.isdn4linux.de (subscribers-only)
-W:     http://www.melware.de
-S:     Maintained
-F:     drivers/isdn/hardware/eicon/
-
 IT87 HARDWARE MONITORING DRIVER
 M:     Jean Delvare <jdelvare@suse.com>
 L:     linux-hwmon@vger.kernel.org
@@ -10326,8 +10320,8 @@ NETERION 10GbE DRIVERS (s2io/vxge)
 M:     Jon Mason <jdmason@kudzu.us>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/s2io.txt
-F:     Documentation/networking/vxge.txt
+F:     Documentation/networking/device_drivers/neterion/s2io.txt
+F:     Documentation/networking/device_drivers/neterion/vxge.txt
 F:     drivers/net/ethernet/neterion/
 
 NETFILTER
@@ -10769,6 +10763,14 @@ L:     linux-nfc@lists.01.org (moderated for non-subscribers)
 S:     Supported
 F:     drivers/nfc/nxp-nci
 
+OBJAGG
+M:     Jiri Pirko <jiri@mellanox.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     lib/objagg.c
+F:     lib/test_objagg.c
+F:     include/linux/objagg.h
+
 OBJTOOL
 M:     Josh Poimboeuf <jpoimboe@redhat.com>
 M:     Peter Zijlstra <peterz@infradead.org>
@@ -12329,7 +12331,7 @@ QLOGIC QLA3XXX NETWORK DRIVER
 M:     Dept-GELinuxNICDev@cavium.com
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/LICENSE.qla3xxx
+F:     Documentation/networking/device_drivers/qlogic/LICENSE.qla3xxx
 F:     drivers/net/ethernet/qlogic/qla3xxx.*
 
 QLOGIC QLA4XXX iSCSI DRIVER
@@ -12381,7 +12383,7 @@ L:      linux-kernel@vger.kernel.org
 S:     Maintained
 F:     drivers/bus/fsl-mc/
 F:     Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt
-F:     Documentation/networking/dpaa2/overview.rst
+F:     Documentation/networking/device_drivers/freescale/dpaa2/overview.rst
 
 QT1010 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
@@ -14121,7 +14123,7 @@ SPIDERNET NETWORK DRIVER for CELL
 M:     Ishizaki Kou <kou.ishizaki@toshiba.co.jp>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/spider_net.txt
+F:     Documentation/networking/device_drivers/toshiba/spider_net.txt
 F:     drivers/net/ethernet/toshiba/spider_net*
 
 SPMI SUBSYSTEM
@@ -15111,7 +15113,7 @@ M:      Samuel Chessman <chessman@tux.org>
 L:     tlan-devel@lists.sourceforge.net (subscribers-only)
 W:     http://sourceforge.net/projects/tlan/
 S:     Maintained
-F:     Documentation/networking/tlan.txt
+F:     Documentation/networking/device_drivers/ti/tlan.txt
 F:     drivers/net/ethernet/ti/tlan.*
 
 TM6000 VIDEO4LINUX DRIVER
index 4d8cb9b..3a0e34f 100644 (file)
@@ -1159,19 +1159,19 @@ jmp_cmp:
                        emit_load(r_A, r_skb, off, ctx);
                        break;
                case BPF_ANC | SKF_AD_VLAN_TAG:
-               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        ctx->flags |= SEEN_SKB | SEEN_A;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  vlan_tci) != 2);
                        off = offsetof(struct sk_buff, vlan_tci);
-                       emit_half_load_unsigned(r_s0, r_skb, off, ctx);
-                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                               emit_andi(r_A, r_s0, (u16)~VLAN_TAG_PRESENT, ctx);
-                       } else {
-                               emit_andi(r_A, r_s0, VLAN_TAG_PRESENT, ctx);
-                               /* return 1 if present */
-                               emit_sltu(r_A, r_zero, r_A, ctx);
-                       }
+                       emit_half_load_unsigned(r_A, r_skb, off, ctx);
+                       break;
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                       ctx->flags |= SEEN_SKB | SEEN_A;
+                       emit_load_byte(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET(), ctx);
+                       if (PKT_VLAN_PRESENT_BIT)
+                               emit_srl(r_A, r_A, PKT_VLAN_PRESENT_BIT, ctx);
+                       if (PKT_VLAN_PRESENT_BIT < 7)
+                               emit_andi(r_A, r_A, 1, ctx);
                        break;
                case BPF_ANC | SKF_AD_PKTTYPE:
                        ctx->flags |= SEEN_SKB;
index d5bfe24..91d223c 100644 (file)
@@ -379,18 +379,17 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
                                                          hash));
                        break;
                case BPF_ANC | SKF_AD_VLAN_TAG:
-               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-                       BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          vlan_tci));
-                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                               PPC_ANDI(r_A, r_A, ~VLAN_TAG_PRESENT);
-                       } else {
-                               PPC_ANDI(r_A, r_A, VLAN_TAG_PRESENT);
-                               PPC_SRWI(r_A, r_A, 12);
-                       }
+                       break;
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                       PPC_LBZ_OFFS(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET());
+                       if (PKT_VLAN_PRESENT_BIT)
+                               PPC_SRWI(r_A, r_A, PKT_VLAN_PRESENT_BIT);
+                       if (PKT_VLAN_PRESENT_BIT < 7)
+                               PPC_ANDI(r_A, r_A, 1);
                        break;
                case BPF_ANC | SKF_AD_QUEUE:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
index a5ff886..84cc8f7 100644 (file)
@@ -552,15 +552,14 @@ void bpf_jit_compile(struct bpf_prog *fp)
                                emit_skb_load32(hash, r_A);
                                break;
                        case BPF_ANC | SKF_AD_VLAN_TAG:
-                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                                emit_skb_load16(vlan_tci, r_A);
-                               if (code != (BPF_ANC | SKF_AD_VLAN_TAG)) {
-                                       emit_alu_K(SRL, 12);
+                               break;
+                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
+                               __emit_skb_load8(__pkt_vlan_present_offset, r_A);
+                               if (PKT_VLAN_PRESENT_BIT)
+                                       emit_alu_K(SRL, PKT_VLAN_PRESENT_BIT);
+                               if (PKT_VLAN_PRESENT_BIT < 7)
                                        emit_andi(r_A, 1, r_A);
-                               } else {
-                                       emit_loadimm(~VLAN_TAG_PRESENT, r_TMP);
-                                       emit_and(r_A, r_TMP, r_A);
-                               }
                                break;
                        case BPF_LD | BPF_W | BPF_LEN:
                                emit_skb_load32(len, r_A);
index f55ffde..14053e0 100644 (file)
@@ -754,8 +754,8 @@ static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
 
        regs = of_get_property(op->dev.of_node, "reg", NULL);
 
-       return sprintf(page, "   SBUS slot/device:\t\t%d/'%s'\n",
-                      (regs ? regs->which_io : 0), op->dev.of_node->name);
+       return sprintf(page, "   SBUS slot/device:\t\t%d/'%pOFn'\n",
+                      (regs ? regs->which_io : 0), op->dev.of_node);
 }
 
 static const struct fore200e_bus fore200e_sbus_ops = {
index 20209e2..228b91b 100644 (file)
@@ -1074,8 +1074,7 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
        csk->txq_idx = (rxq_idx < cdev->lldi->ntxq) ? rxq_idx :
                        port_id * step;
        csk->sndbuf = newsk->sk_sndbuf;
-       csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi->adapter_type,
-                                        cxgb4_port_viid(ndev));
+       csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx;
        tp->rcv_wnd = select_rcv_wnd(csk);
        RCV_WSCALE(tp) = select_rcv_wscale(tcp_full_space(newsk),
                                           WSCALE_OK(tp),
index 615413b..97ecc8c 100644 (file)
@@ -2058,8 +2058,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                }
                ep->mtu = pdev->mtu;
                ep->tx_chan = cxgb4_port_chan(pdev);
-               ep->smac_idx = cxgb4_tp_smt_idx(adapter_type,
-                                               cxgb4_port_viid(pdev));
+               ep->smac_idx = ((struct port_info *)netdev_priv(pdev))->smt_idx;
                step = cdev->rdev.lldi.ntxq /
                        cdev->rdev.lldi.nchan;
                ep->txq_idx = cxgb4_port_idx(pdev) * step;
@@ -2078,8 +2077,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                        goto out;
                ep->mtu = dst_mtu(dst);
                ep->tx_chan = cxgb4_port_chan(pdev);
-               ep->smac_idx = cxgb4_tp_smt_idx(adapter_type,
-                                               cxgb4_port_viid(pdev));
+               ep->smac_idx = ((struct port_info *)netdev_priv(pdev))->smt_idx;
                step = cdev->rdev.lldi.ntxq /
                        cdev->rdev.lldi.nchan;
                ep->txq_idx = cxgb4_port_idx(pdev) * step;
@@ -3944,7 +3942,7 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
        } else {
                vlan_eh = (struct vlan_ethhdr *)(req + 1);
                iph = (struct iphdr *)(vlan_eh + 1);
-               skb->vlan_tci = ntohs(cpl->vlan);
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(cpl->vlan));
        }
 
        if (iph->version != 0x4)
index 771eb6b..4b3999d 100644 (file)
@@ -404,7 +404,7 @@ static struct i40iw_puda_buf *i40iw_form_cm_frame(struct i40iw_cm_node *cm_node,
        if (pdata)
                pd_len = pdata->size;
 
-       if (cm_node->vlan_id < VLAN_TAG_PRESENT)
+       if (cm_node->vlan_id <= VLAN_VID_MASK)
                eth_hlen += 4;
 
        if (cm_node->ipv4)
@@ -433,7 +433,7 @@ static struct i40iw_puda_buf *i40iw_form_cm_frame(struct i40iw_cm_node *cm_node,
 
                ether_addr_copy(ethh->h_dest, cm_node->rem_mac);
                ether_addr_copy(ethh->h_source, cm_node->loc_mac);
-               if (cm_node->vlan_id < VLAN_TAG_PRESENT) {
+               if (cm_node->vlan_id <= VLAN_VID_MASK) {
                        ((struct vlan_ethhdr *)ethh)->h_vlan_proto = htons(ETH_P_8021Q);
                        vtag = (cm_node->user_pri << VLAN_PRIO_SHIFT) | cm_node->vlan_id;
                        ((struct vlan_ethhdr *)ethh)->h_vlan_TCI = htons(vtag);
@@ -463,7 +463,7 @@ static struct i40iw_puda_buf *i40iw_form_cm_frame(struct i40iw_cm_node *cm_node,
 
                ether_addr_copy(ethh->h_dest, cm_node->rem_mac);
                ether_addr_copy(ethh->h_source, cm_node->loc_mac);
-               if (cm_node->vlan_id < VLAN_TAG_PRESENT) {
+               if (cm_node->vlan_id <= VLAN_VID_MASK) {
                        ((struct vlan_ethhdr *)ethh)->h_vlan_proto = htons(ETH_P_8021Q);
                        vtag = (cm_node->user_pri << VLAN_PRIO_SHIFT) | cm_node->vlan_id;
                        ((struct vlan_ethhdr *)ethh)->h_vlan_TCI = htons(vtag);
@@ -3323,7 +3323,7 @@ static void i40iw_init_tcp_ctx(struct i40iw_cm_node *cm_node,
 
        tcp_info->flow_label = 0;
        tcp_info->snd_mss = cpu_to_le32(((u32)cm_node->tcp_cntxt.mss));
-       if (cm_node->vlan_id < VLAN_TAG_PRESENT) {
+       if (cm_node->vlan_id <= VLAN_VID_MASK) {
                tcp_info->insert_vlan_tag = true;
                tcp_info->vlan_tag = cpu_to_le16(((u16)cm_node->user_pri << I40IW_VLAN_PRIO_SHIFT) |
                                                  cm_node->vlan_id);
index 82adc0d..4351234 100644 (file)
@@ -181,6 +181,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
        struct mlx4_ib_dev *dev = to_mdev(ibdev);
        struct mlx4_ib_cq *cq;
        struct mlx4_uar *uar;
+       void *buf_addr;
        int err;
 
        if (entries < 1 || entries > dev->dev->caps.max_cqes)
@@ -211,6 +212,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
                        goto err_cq;
                }
 
+               buf_addr = (void *)(unsigned long)ucmd.buf_addr;
+
                err = mlx4_ib_get_cq_umem(dev, context, &cq->buf, &cq->umem,
                                          ucmd.buf_addr, entries);
                if (err)
@@ -237,6 +240,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
                if (err)
                        goto err_db;
 
+               buf_addr = &cq->buf.buf;
+
                uar = &dev->priv_uar;
                cq->mcq.usage = MLX4_RES_USAGE_DRIVER;
        }
@@ -246,7 +251,9 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
 
        err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
                            cq->db.dma, &cq->mcq, vector, 0,
-                           !!(cq->create_flags & IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION));
+                           !!(cq->create_flags &
+                              IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION),
+                           buf_addr, !!context);
        if (err)
                goto err_dbmap;
 
index e96ffff..cc4dce5 100644 (file)
@@ -223,11 +223,11 @@ static struct sk_buff *nes_get_next_skb(struct nes_device *nesdev, struct nes_qp
                }
 
                old_skb = skb;
-               skb = skb->next;
+               skb = skb_peek_next(skb, &nesqp->pau_list);
                skb_unlink(old_skb, &nesqp->pau_list);
                nes_mgt_free_skb(nesdev, old_skb, PCI_DMA_TODEVICE);
                nes_rem_ref_cm_node(nesqp->cm_node);
-               if (skb == (struct sk_buff *)&nesqp->pau_list)
+               if (!skb)
                        goto out;
        }
        return skb;
@@ -551,14 +551,14 @@ static void queue_fpdus(struct sk_buff *skb, struct nes_vnic *nesvnic, struct ne
 
        /* Queue skb by sequence number */
        if (skb_queue_len(&nesqp->pau_list) == 0) {
-               skb_queue_head(&nesqp->pau_list, skb);
+               __skb_queue_head(&nesqp->pau_list, skb);
        } else {
                skb_queue_walk(&nesqp->pau_list, tmpskb) {
                        cb = (struct nes_rskb_cb *)&tmpskb->cb[0];
                        if (before(seqnum, cb->seqnum))
                                break;
                }
-               skb_insert(tmpskb, skb, &nesqp->pau_list);
+               __skb_insert(skb, tmpskb->prev, tmpskb, &nesqp->pau_list);
        }
        if (nesqp->pau_state == PAU_READY)
                process_it = true;
index 8710214..6214d8c 100644 (file)
@@ -167,7 +167,7 @@ int ipoib_open(struct net_device *dev)
                        if (flags & IFF_UP)
                                continue;
 
-                       dev_change_flags(cpriv->dev, flags | IFF_UP);
+                       dev_change_flags(cpriv->dev, flags | IFF_UP, NULL);
                }
                up_read(&priv->vlan_rwsem);
        }
@@ -207,7 +207,7 @@ static int ipoib_stop(struct net_device *dev)
                        if (!(flags & IFF_UP))
                                continue;
 
-                       dev_change_flags(cpriv->dev, flags & ~IFF_UP);
+                       dev_change_flags(cpriv->dev, flags & ~IFF_UP, NULL);
                }
                up_read(&priv->vlan_rwsem);
        }
@@ -1823,7 +1823,7 @@ static void ipoib_parent_unregister_pre(struct net_device *ndev)
         * running ensures the it will not add more work.
         */
        rtnl_lock();
-       dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
+       dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP, NULL);
        rtnl_unlock();
 
        /* ipoib_event() cannot be running once this returns */
index 30d028d..95c4030 100644 (file)
@@ -5,5 +5,3 @@ comment "CAPI hardware drivers"
 
 source "drivers/isdn/hardware/avm/Kconfig"
 
-source "drivers/isdn/hardware/eicon/Kconfig"
-
index a5d8fce..e503032 100644 (file)
@@ -3,5 +3,4 @@
 # Object files in subdirectories
 
 obj-$(CONFIG_CAPI_AVM)         += avm/
-obj-$(CONFIG_CAPI_EICON)       += eicon/
 obj-$(CONFIG_MISDN)            += mISDN/
diff --git a/drivers/isdn/hardware/eicon/Kconfig b/drivers/isdn/hardware/eicon/Kconfig
deleted file mode 100644 (file)
index 6082b6a..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# ISDN DIVAS Eicon driver
-#
-
-menuconfig CAPI_EICON
-       bool "Active Eicon DIVA Server cards"
-       help
-         Enable support for Eicon Networks active ISDN cards.
-
-if CAPI_EICON
-
-config ISDN_DIVAS
-       tristate "Support Eicon DIVA Server cards"
-       depends on PROC_FS && PCI
-       help
-         Say Y here if you have an Eicon Networks DIVA Server PCI ISDN card.
-         In order to use this card, additional firmware is necessary, which
-         has to be downloaded into the card using the divactrl utility.
-
-if ISDN_DIVAS
-
-config ISDN_DIVAS_BRIPCI
-       bool "DIVA Server BRI/PCI support"
-       help
-         Enable support for DIVA Server BRI-PCI.
-
-config ISDN_DIVAS_PRIPCI
-       bool "DIVA Server PRI/PCI support"
-       help
-         Enable support for DIVA Server PRI-PCI.
-
-config ISDN_DIVAS_DIVACAPI
-       tristate "DIVA CAPI2.0 interface support"
-       help
-         You need this to provide the CAPI interface
-         for DIVA Server cards.
-
-config ISDN_DIVAS_USERIDI
-       tristate "DIVA User-IDI interface support"
-       help
-         Enable support for user-mode IDI interface.
-
-config ISDN_DIVAS_MAINT
-       tristate "DIVA Maint driver support"
-       depends on m
-       help
-         Enable Divas Maintenance driver.
-
-endif # ISDN_DIVAS
-
-endif # CAPI_EICON
diff --git a/drivers/isdn/hardware/eicon/Makefile b/drivers/isdn/hardware/eicon/Makefile
deleted file mode 100644 (file)
index a0ab2e2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Makefile for the Eicon DIVA ISDN drivers.
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_ISDN_DIVAS)               += divadidd.o divas.o
-obj-$(CONFIG_ISDN_DIVAS_MAINT)         += diva_mnt.o
-obj-$(CONFIG_ISDN_DIVAS_USERIDI)       += diva_idi.o
-obj-$(CONFIG_ISDN_DIVAS_DIVACAPI)      += divacapi.o
-
-# Multipart objects. 
-
-divas-y                                        := divasmain.o divasfunc.o di.o io.o istream.o \
-                                          diva.o divasproc.o diva_dma.o
-divas-$(CONFIG_ISDN_DIVAS_BRIPCI)      += os_bri.o  s_bri.o os_4bri.o s_4bri.o
-divas-$(CONFIG_ISDN_DIVAS_PRIPCI)      += os_pri.o  s_pri.o
-
-divacapi-y                             := capimain.o capifunc.o message.o capidtmf.o
-
-divadidd-y                             := diva_didd.o diddfunc.o dadapter.o
-
-diva_mnt-y                             := divamnt.o mntfunc.o debug.o maintidi.o
-
-diva_idi-y                             := divasi.o idifunc.o um_idi.o dqueue.o
diff --git a/drivers/isdn/hardware/eicon/adapter.h b/drivers/isdn/hardware/eicon/adapter.h
deleted file mode 100644 (file)
index f9b24eb..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: adapter.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
-
-#ifndef __DIVA_USER_MODE_IDI_ADAPTER_H__
-#define __DIVA_USER_MODE_IDI_ADAPTER_H__
-
-#define DIVA_UM_IDI_ADAPTER_REMOVED 0x00000001
-
-typedef struct _diva_um_idi_adapter {
-       struct list_head link;
-       DESCRIPTOR d;
-       int adapter_nr;
-       struct list_head entity_q;      /* entities linked to this adapter */
-       dword status;
-} diva_um_idi_adapter_t;
-
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/capi20.h b/drivers/isdn/hardware/eicon/capi20.h
deleted file mode 100644 (file)
index 391e417..0000000
+++ /dev/null
@@ -1,699 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef _INC_CAPI20
-#define _INC_CAPI20
-/* operations on message queues                             */
-/* the common device type for CAPI20 drivers */
-#define FILE_DEVICE_CAPI20 0x8001
-/* DEVICE_CONTROL codes for user and kernel mode applications */
-#define CAPI20_CTL_REGISTER             0x0801
-#define CAPI20_CTL_RELEASE              0x0802
-#define CAPI20_CTL_GET_MANUFACTURER     0x0805
-#define CAPI20_CTL_GET_VERSION          0x0806
-#define CAPI20_CTL_GET_SERIAL           0x0807
-#define CAPI20_CTL_GET_PROFILE          0x0808
-/* INTERNAL_DEVICE_CONTROL codes for kernel mode applicatios only */
-#define CAPI20_CTL_PUT_MESSAGE          0x0803
-#define CAPI20_CTL_GET_MESSAGE          0x0804
-/* the wrapped codes as required by the system */
-#define CAPI_CTL_CODE(f, m)             CTL_CODE(FILE_DEVICE_CAPI20, f, m, FILE_ANY_ACCESS)
-#define IOCTL_CAPI_REGISTER             CAPI_CTL_CODE(CAPI20_CTL_REGISTER, METHOD_BUFFERED)
-#define IOCTL_CAPI_RELEASE              CAPI_CTL_CODE(CAPI20_CTL_RELEASE, METHOD_BUFFERED)
-#define IOCTL_CAPI_GET_MANUFACTURER     CAPI_CTL_CODE(CAPI20_CTL_GET_MANUFACTURER, METHOD_BUFFERED)
-#define IOCTL_CAPI_GET_VERSION          CAPI_CTL_CODE(CAPI20_CTL_GET_VERSION, METHOD_BUFFERED)
-#define IOCTL_CAPI_GET_SERIAL           CAPI_CTL_CODE(CAPI20_CTL_GET_SERIAL, METHOD_BUFFERED)
-#define IOCTL_CAPI_GET_PROFILE          CAPI_CTL_CODE(CAPI20_CTL_GET_PROFILE, METHOD_BUFFERED)
-#define IOCTL_CAPI_PUT_MESSAGE          CAPI_CTL_CODE(CAPI20_CTL_PUT_MESSAGE, METHOD_BUFFERED)
-#define IOCTL_CAPI_GET_MESSAGE          CAPI_CTL_CODE(CAPI20_CTL_GET_MESSAGE, METHOD_BUFFERED)
-struct divas_capi_register_params  {
-       word MessageBufferSize;
-       word maxLogicalConnection;
-       word maxBDataBlocks;
-       word maxBDataLen;
-};
-struct divas_capi_version  {
-       word CapiMajor;
-       word CapiMinor;
-       word ManuMajor;
-       word ManuMinor;
-};
-typedef struct api_profile_s {
-       word          Number;
-       word          Channels;
-       dword         Global_Options;
-       dword         B1_Protocols;
-       dword         B2_Protocols;
-       dword         B3_Protocols;
-} API_PROFILE;
-/* ISDN Common API message types                            */
-#define _ALERT_R                        0x8001
-#define _CONNECT_R                      0x8002
-#define _CONNECT_I                      0x8202
-#define _CONNECT_ACTIVE_I               0x8203
-#define _DISCONNECT_R                   0x8004
-#define _DISCONNECT_I                   0x8204
-#define _LISTEN_R                       0x8005
-#define _INFO_R                         0x8008
-#define _INFO_I                         0x8208
-#define _SELECT_B_REQ                   0x8041
-#define _FACILITY_R                     0x8080
-#define _FACILITY_I                     0x8280
-#define _CONNECT_B3_R                   0x8082
-#define _CONNECT_B3_I                   0x8282
-#define _CONNECT_B3_ACTIVE_I            0x8283
-#define _DISCONNECT_B3_R                0x8084
-#define _DISCONNECT_B3_I                0x8284
-#define _DATA_B3_R                      0x8086
-#define _DATA_B3_I                      0x8286
-#define _RESET_B3_R                     0x8087
-#define _RESET_B3_I                     0x8287
-#define _CONNECT_B3_T90_ACTIVE_I        0x8288
-#define _MANUFACTURER_R                 0x80ff
-#define _MANUFACTURER_I                 0x82ff
-/* OR this to convert a REQUEST to a CONFIRM                */
-#define CONFIRM                 0x0100
-/* OR this to convert a INDICATION to a RESPONSE            */
-#define RESPONSE                0x0100
-/*------------------------------------------------------------------*/
-/* diehl isdn private MANUFACTURER codes                            */
-/*------------------------------------------------------------------*/
-#define _DI_MANU_ID             0x44444944
-#define _DI_ASSIGN_PLCI         0x0001
-#define _DI_ADV_CODEC           0x0002
-#define _DI_DSP_CTRL            0x0003
-#define _DI_SIG_CTRL            0x0004
-#define _DI_RXT_CTRL            0x0005
-#define _DI_IDI_CTRL            0x0006
-#define _DI_CFG_CTRL            0x0007
-#define _DI_REMOVE_CODEC        0x0008
-#define _DI_OPTIONS_REQUEST     0x0009
-#define _DI_SSEXT_CTRL          0x000a
-#define _DI_NEGOTIATE_B3        0x000b
-/*------------------------------------------------------------------*/
-/* parameter structures                                             */
-/*------------------------------------------------------------------*/
-/* ALERT-REQUEST                                            */
-typedef struct {
-       byte structs[0];      /* Additional Info */
-} _ALT_REQP;
-/* ALERT-CONFIRM                                            */
-typedef struct {
-       word Info;
-} _ALT_CONP;
-/* CONNECT-REQUEST                                          */
-typedef struct {
-       word CIP_Value;
-       byte structs[0];      /* Called party number,
-                                Called party subaddress,
-                                Calling party number,
-                                Calling party subaddress,
-                                B_protocol,
-                                BC,
-                                LLC,
-                                HLC,
-                                Additional Info */
-} _CON_REQP;
-/* CONNECT-CONFIRM                                          */
-typedef struct {
-       word Info;
-} _CON_CONP;
-/* CONNECT-INDICATION                                       */
-typedef struct {
-       word CIP_Value;
-       byte structs[0];      /* Called party number,
-                                Called party subaddress,
-                                Calling party number,
-                                Calling party subaddress,
-                                BC,
-                                LLC,
-                                HLC,
-                                Additional Info */
-} _CON_INDP;
-/* CONNECT-RESPONSE                                         */
-typedef struct {
-       word Accept;
-       byte structs[0];      /* B_protocol,
-                                Connected party number,
-                                Connected party subaddress,
-                                LLC */
-} _CON_RESP;
-/* CONNECT-ACTIVE-INDICATION                                */
-typedef struct {
-       byte structs[0];      /* Connected party number,
-                                Connected party subaddress,
-                                LLC */
-} _CON_A_INDP;
-/* CONNECT-ACTIVE-RESPONSE                                  */
-typedef struct {
-       byte structs[0];      /* empty */
-} _CON_A_RESP;
-/* DISCONNECT-REQUEST                                       */
-typedef struct {
-       byte structs[0];      /* Additional Info */
-} _DIS_REQP;
-/* DISCONNECT-CONFIRM                                       */
-typedef struct {
-       word Info;
-} _DIS_CONP;
-/* DISCONNECT-INDICATION                                    */
-typedef struct {
-       word Info;
-} _DIS_INDP;
-/* DISCONNECT-RESPONSE                                      */
-typedef struct {
-       byte structs[0];      /* empty */
-} _DIS_RESP;
-/* LISTEN-REQUEST                                           */
-typedef struct {
-       dword Info_Mask;
-       dword CIP_Mask;
-       byte structs[0];      /* Calling party number,
-                                Calling party subaddress */
-} _LIS_REQP;
-/* LISTEN-CONFIRM                                           */
-typedef struct {
-       word Info;
-} _LIS_CONP;
-/* INFO-REQUEST                                             */
-typedef struct {
-       byte structs[0];      /* Called party number,
-                                Additional Info */
-} _INF_REQP;
-/* INFO-CONFIRM                                             */
-typedef struct {
-       word Info;
-} _INF_CONP;
-/* INFO-INDICATION                                          */
-typedef struct {
-       word Number;
-       byte structs[0];      /* Info element */
-} _INF_INDP;
-/* INFO-RESPONSE                                            */
-typedef struct {
-       byte structs[0];      /* empty */
-} _INF_RESP;
-/* SELECT-B-REQUEST                                         */
-typedef struct {
-       byte structs[0];      /* B-protocol */
-} _SEL_B_REQP;
-/* SELECT-B-CONFIRM                                         */
-typedef struct {
-       word Info;
-} _SEL_B_CONP;
-/* FACILITY-REQUEST */
-typedef struct {
-       word Selector;
-       byte structs[0];      /* Facility parameters */
-} _FAC_REQP;
-/* FACILITY-CONFIRM STRUCT FOR SUPPLEMENT. SERVICES */
-typedef struct {
-       byte  struct_length;
-       word  function;
-       byte  length;
-       word  SupplementaryServiceInfo;
-       dword SupportedServices;
-} _FAC_CON_STRUCTS;
-/* FACILITY-CONFIRM */
-typedef struct {
-       word Info;
-       word Selector;
-       byte structs[0];      /* Facility parameters */
-} _FAC_CONP;
-/* FACILITY-INDICATION */
-typedef struct {
-       word Selector;
-       byte structs[0];      /* Facility parameters */
-} _FAC_INDP;
-/* FACILITY-RESPONSE */
-typedef struct {
-       word Selector;
-       byte structs[0];      /* Facility parameters */
-} _FAC_RESP;
-/* CONNECT-B3-REQUEST                                       */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _CON_B3_REQP;
-/* CONNECT-B3-CONFIRM                                       */
-typedef struct {
-       word Info;
-} _CON_B3_CONP;
-/* CONNECT-B3-INDICATION                                    */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _CON_B3_INDP;
-/* CONNECT-B3-RESPONSE                                      */
-typedef struct {
-       word Accept;
-       byte structs[0];      /* NCPI */
-} _CON_B3_RESP;
-/* CONNECT-B3-ACTIVE-INDICATION                             */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _CON_B3_A_INDP;
-/* CONNECT-B3-ACTIVE-RESPONSE                               */
-typedef struct {
-       byte structs[0];      /* empty */
-} _CON_B3_A_RESP;
-/* DISCONNECT-B3-REQUEST                                    */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _DIS_B3_REQP;
-/* DISCONNECT-B3-CONFIRM                                    */
-typedef struct {
-       word Info;
-} _DIS_B3_CONP;
-/* DISCONNECT-B3-INDICATION                                 */
-typedef struct {
-       word Info;
-       byte structs[0];      /* NCPI */
-} _DIS_B3_INDP;
-/* DISCONNECT-B3-RESPONSE                                   */
-typedef struct {
-       byte structs[0];      /* empty */
-} _DIS_B3_RESP;
-/* DATA-B3-REQUEST                                          */
-typedef struct {
-       dword         Data;
-       word          Data_Length;
-       word          Number;
-       word          Flags;
-} _DAT_B3_REQP;
-/* DATA-B3-REQUEST 64 BIT Systems                           */
-typedef struct {
-       dword         Data;
-       word          Data_Length;
-       word          Number;
-       word          Flags;
-       void          *pData;
-} _DAT_B3_REQ64P;
-/* DATA-B3-CONFIRM                                          */
-typedef struct {
-       word          Number;
-       word          Info;
-} _DAT_B3_CONP;
-/* DATA-B3-INDICATION                                       */
-typedef struct {
-       dword         Data;
-       word          Data_Length;
-       word          Number;
-       word          Flags;
-} _DAT_B3_INDP;
-/* DATA-B3-INDICATION  64 BIT Systems                       */
-typedef struct {
-       dword         Data;
-       word          Data_Length;
-       word          Number;
-       word          Flags;
-       void          *pData;
-} _DAT_B3_IND64P;
-/* DATA-B3-RESPONSE                                         */
-typedef struct {
-       word          Number;
-} _DAT_B3_RESP;
-/* RESET-B3-REQUEST                                         */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _RES_B3_REQP;
-/* RESET-B3-CONFIRM                                         */
-typedef struct {
-       word Info;
-} _RES_B3_CONP;
-/* RESET-B3-INDICATION                                      */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _RES_B3_INDP;
-/* RESET-B3-RESPONSE                                        */
-typedef struct {
-       byte structs[0];      /* empty */
-} _RES_B3_RESP;
-/* CONNECT-B3-T90-ACTIVE-INDICATION                         */
-typedef struct {
-       byte structs[0];      /* NCPI */
-} _CON_B3_T90_A_INDP;
-/* CONNECT-B3-T90-ACTIVE-RESPONSE                           */
-typedef struct {
-       word Reject;
-       byte structs[0];      /* NCPI */
-} _CON_B3_T90_A_RESP;
-/*------------------------------------------------------------------*/
-/* message structure                                                */
-/*------------------------------------------------------------------*/
-typedef struct _API_MSG CAPI_MSG;
-typedef struct _MSG_HEADER CAPI_MSG_HEADER;
-struct _API_MSG {
-       struct _MSG_HEADER {
-               word        length;
-               word        appl_id;
-               word        command;
-               word        number;
-               byte        controller;
-               byte        plci;
-               word        ncci;
-       } header;
-       union {
-               _ALT_REQP           alert_req;
-               _ALT_CONP           alert_con;
-               _CON_REQP           connect_req;
-               _CON_CONP           connect_con;
-               _CON_INDP           connect_ind;
-               _CON_RESP           connect_res;
-               _CON_A_INDP         connect_a_ind;
-               _CON_A_RESP         connect_a_res;
-               _DIS_REQP           disconnect_req;
-               _DIS_CONP           disconnect_con;
-               _DIS_INDP           disconnect_ind;
-               _DIS_RESP           disconnect_res;
-               _LIS_REQP           listen_req;
-               _LIS_CONP           listen_con;
-               _INF_REQP           info_req;
-               _INF_CONP           info_con;
-               _INF_INDP           info_ind;
-               _INF_RESP           info_res;
-               _SEL_B_REQP         select_b_req;
-               _SEL_B_CONP         select_b_con;
-               _FAC_REQP           facility_req;
-               _FAC_CONP           facility_con;
-               _FAC_INDP           facility_ind;
-               _FAC_RESP           facility_res;
-               _CON_B3_REQP        connect_b3_req;
-               _CON_B3_CONP        connect_b3_con;
-               _CON_B3_INDP        connect_b3_ind;
-               _CON_B3_RESP        connect_b3_res;
-               _CON_B3_A_INDP      connect_b3_a_ind;
-               _CON_B3_A_RESP      connect_b3_a_res;
-               _DIS_B3_REQP        disconnect_b3_req;
-               _DIS_B3_CONP        disconnect_b3_con;
-               _DIS_B3_INDP        disconnect_b3_ind;
-               _DIS_B3_RESP        disconnect_b3_res;
-               _DAT_B3_REQP        data_b3_req;
-               _DAT_B3_REQ64P      data_b3_req64;
-               _DAT_B3_CONP        data_b3_con;
-               _DAT_B3_INDP        data_b3_ind;
-               _DAT_B3_IND64P      data_b3_ind64;
-               _DAT_B3_RESP        data_b3_res;
-               _RES_B3_REQP        reset_b3_req;
-               _RES_B3_CONP        reset_b3_con;
-               _RES_B3_INDP        reset_b3_ind;
-               _RES_B3_RESP        reset_b3_res;
-               _CON_B3_T90_A_INDP  connect_b3_t90_a_ind;
-               _CON_B3_T90_A_RESP  connect_b3_t90_a_res;
-               byte                b[200];
-       } info;
-};
-/*------------------------------------------------------------------*/
-/* non-fatal errors                                                 */
-/*------------------------------------------------------------------*/
-#define _NCPI_IGNORED           0x0001
-#define _FLAGS_IGNORED          0x0002
-#define _ALERT_IGNORED          0x0003
-/*------------------------------------------------------------------*/
-/* API function error codes                                         */
-/*------------------------------------------------------------------*/
-#define GOOD                            0x0000
-#define _TOO_MANY_APPLICATIONS          0x1001
-#define _BLOCK_TOO_SMALL                0x1002
-#define _BUFFER_TOO_BIG                 0x1003
-#define _MSG_BUFFER_TOO_SMALL           0x1004
-#define _TOO_MANY_CONNECTIONS           0x1005
-#define _REG_CAPI_BUSY                  0x1007
-#define _REG_RESOURCE_ERROR             0x1008
-#define _REG_CAPI_NOT_INSTALLED         0x1009
-#define _WRONG_APPL_ID                  0x1101
-#define _BAD_MSG                        0x1102
-#define _QUEUE_FULL                     0x1103
-#define _GET_NO_MSG                     0x1104
-#define _MSG_LOST                       0x1105
-#define _WRONG_NOTIFY                   0x1106
-#define _CAPI_BUSY                      0x1107
-#define _RESOURCE_ERROR                 0x1108
-#define _CAPI_NOT_INSTALLED             0x1109
-#define _NO_EXTERNAL_EQUIPMENT          0x110a
-#define _ONLY_EXTERNAL_EQUIPMENT        0x110b
-/*------------------------------------------------------------------*/
-/* addressing/coding error codes                                    */
-/*------------------------------------------------------------------*/
-#define _WRONG_STATE                    0x2001
-#define _WRONG_IDENTIFIER               0x2002
-#define _OUT_OF_PLCI                    0x2003
-#define _OUT_OF_NCCI                    0x2004
-#define _OUT_OF_LISTEN                  0x2005
-#define _OUT_OF_FAX                     0x2006
-#define _WRONG_MESSAGE_FORMAT           0x2007
-#define _OUT_OF_INTERCONNECT_RESOURCES  0x2008
-/*------------------------------------------------------------------*/
-/* configuration error codes                                        */
-/*------------------------------------------------------------------*/
-#define _B1_NOT_SUPPORTED                    0x3001
-#define _B2_NOT_SUPPORTED                    0x3002
-#define _B3_NOT_SUPPORTED                    0x3003
-#define _B1_PARM_NOT_SUPPORTED               0x3004
-#define _B2_PARM_NOT_SUPPORTED               0x3005
-#define _B3_PARM_NOT_SUPPORTED               0x3006
-#define _B_STACK_NOT_SUPPORTED               0x3007
-#define _NCPI_NOT_SUPPORTED                  0x3008
-#define _CIP_NOT_SUPPORTED                   0x3009
-#define _FLAGS_NOT_SUPPORTED                 0x300a
-#define _FACILITY_NOT_SUPPORTED              0x300b
-#define _DATA_LEN_NOT_SUPPORTED              0x300c
-#define _RESET_NOT_SUPPORTED                 0x300d
-#define _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED 0x300e
-#define _REQUEST_NOT_ALLOWED_IN_THIS_STATE   0x3010
-#define _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP 0x3011
-/*------------------------------------------------------------------*/
-/* reason codes                                                     */
-/*------------------------------------------------------------------*/
-#define _L1_ERROR                       0x3301
-#define _L2_ERROR                       0x3302
-#define _L3_ERROR                       0x3303
-#define _OTHER_APPL_CONNECTED           0x3304
-#define _CAPI_GUARD_ERROR               0x3305
-#define _L3_CAUSE                       0x3400
-/*------------------------------------------------------------------*/
-/* b3 reason codes                                                  */
-/*------------------------------------------------------------------*/
-#define _B_CHANNEL_LOST                 0x3301
-#define _B2_ERROR                       0x3302
-#define _B3_ERROR                       0x3303
-/*------------------------------------------------------------------*/
-/* fax error codes                                                  */
-/*------------------------------------------------------------------*/
-#define _FAX_NO_CONNECTION              0x3311
-#define _FAX_TRAINING_ERROR             0x3312
-#define _FAX_REMOTE_REJECT              0x3313
-#define _FAX_REMOTE_ABORT               0x3314
-#define _FAX_PROTOCOL_ERROR             0x3315
-#define _FAX_TX_UNDERRUN                0x3316
-#define _FAX_RX_OVERFLOW                0x3317
-#define _FAX_LOCAL_ABORT                0x3318
-#define _FAX_PARAMETER_ERROR            0x3319
-/*------------------------------------------------------------------*/
-/* line interconnect error codes                                    */
-/*------------------------------------------------------------------*/
-#define _LI_USER_INITIATED               0x0000
-#define _LI_LINE_NO_LONGER_AVAILABLE     0x3805
-#define _LI_INTERCONNECT_NOT_ESTABLISHED 0x3806
-#define _LI_LINES_NOT_COMPATIBLE         0x3807
-#define _LI2_USER_INITIATED              0x0000
-#define _LI2_PLCI_HAS_NO_BCHANNEL        0x3800
-#define _LI2_LINES_NOT_COMPATIBLE        0x3801
-#define _LI2_NOT_IN_SAME_INTERCONNECTION 0x3802
-/*------------------------------------------------------------------*/
-/* global options                                                   */
-/*------------------------------------------------------------------*/
-#define GL_INTERNAL_CONTROLLER_SUPPORTED     0x00000001L
-#define GL_EXTERNAL_EQUIPMENT_SUPPORTED      0x00000002L
-#define GL_HANDSET_SUPPORTED                 0x00000004L
-#define GL_DTMF_SUPPORTED                    0x00000008L
-#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED  0x00000010L
-#define GL_CHANNEL_ALLOCATION_SUPPORTED      0x00000020L
-#define GL_BCHANNEL_OPERATION_SUPPORTED      0x00000040L
-#define GL_LINE_INTERCONNECT_SUPPORTED       0x00000080L
-#define GL_ECHO_CANCELLER_SUPPORTED          0x00000100L
-/*------------------------------------------------------------------*/
-/* protocol selection                                               */
-/*------------------------------------------------------------------*/
-#define B1_HDLC                 0
-#define B1_TRANSPARENT          1
-#define B1_V110_ASYNC           2
-#define B1_V110_SYNC            3
-#define B1_T30                  4
-#define B1_HDLC_INVERTED        5
-#define B1_TRANSPARENT_R        6
-#define B1_MODEM_ALL_NEGOTIATE  7
-#define B1_MODEM_ASYNC          8
-#define B1_MODEM_SYNC_HDLC      9
-#define B2_X75                  0
-#define B2_TRANSPARENT          1
-#define B2_SDLC                 2
-#define B2_LAPD                 3
-#define B2_T30                  4
-#define B2_PPP                  5
-#define B2_TRANSPARENT_NO_CRC   6
-#define B2_MODEM_EC_COMPRESSION 7
-#define B2_X75_V42BIS           8
-#define B2_V120_ASYNC           9
-#define B2_V120_ASYNC_V42BIS    10
-#define B2_V120_BIT_TRANSPARENT 11
-#define B2_LAPD_FREE_SAPI_SEL   12
-#define B3_TRANSPARENT          0
-#define B3_T90NL                1
-#define B3_ISO8208              2
-#define B3_X25_DCE              3
-#define B3_T30                  4
-#define B3_T30_WITH_EXTENSIONS  5
-#define B3_RESERVED             6
-#define B3_MODEM                7
-/*------------------------------------------------------------------*/
-/*  facility definitions                                            */
-/*------------------------------------------------------------------*/
-#define SELECTOR_HANDSET            0
-#define SELECTOR_DTMF               1
-#define SELECTOR_V42BIS             2
-#define SELECTOR_SU_SERV            3
-#define SELECTOR_POWER_MANAGEMENT   4
-#define SELECTOR_LINE_INTERCONNECT  5
-#define SELECTOR_ECHO_CANCELLER     6
-/*------------------------------------------------------------------*/
-/*  supplementary services definitions                              */
-/*------------------------------------------------------------------*/
-#define S_GET_SUPPORTED_SERVICES  0x0000
-#define S_LISTEN                  0x0001
-#define S_HOLD                    0x0002
-#define S_RETRIEVE                0x0003
-#define S_SUSPEND                 0x0004
-#define S_RESUME                  0x0005
-#define S_ECT                     0x0006
-#define S_3PTY_BEGIN              0x0007
-#define S_3PTY_END                0x0008
-#define S_CALL_DEFLECTION         0x000d
-#define S_CALL_FORWARDING_START   0x0009
-#define S_CALL_FORWARDING_STOP    0x000a
-#define S_INTERROGATE_DIVERSION   0x000b /* or interrogate parameters */
-#define S_INTERROGATE_NUMBERS     0x000c
-#define S_CCBS_REQUEST            0x000f
-#define S_CCBS_DEACTIVATE         0x0010
-#define S_CCBS_INTERROGATE        0x0011
-#define S_CCBS_CALL               0x0012
-#define S_MWI_ACTIVATE            0x0013
-#define S_MWI_DEACTIVATE          0x0014
-#define S_CONF_BEGIN           0x0017
-#define S_CONF_ADD                0x0018
-#define S_CONF_SPLIT           0x0019
-#define S_CONF_DROP               0x001a
-#define S_CONF_ISOLATE           0x001b
-#define S_CONF_REATTACH           0x001c
-#define S_CCBS_ERASECALLLINKAGEID 0x800d
-#define S_CCBS_STOP_ALERTING      0x8012
-#define S_CCBS_INFO_RETAIN        0x8013
-#define S_MWI_INDICATE            0x8014
-#define S_CONF_PARTYDISC          0x8016
-#define S_CONF_NOTIFICATION       0x8017
-/* Service Masks */
-#define MASK_HOLD_RETRIEVE        0x00000001
-#define MASK_TERMINAL_PORTABILITY 0x00000002
-#define MASK_ECT                  0x00000004
-#define MASK_3PTY                 0x00000008
-#define MASK_CALL_FORWARDING      0x00000010
-#define MASK_CALL_DEFLECTION      0x00000020
-#define MASK_MWI                  0x00000100
-#define MASK_CCNR                 0x00000200
-#define MASK_CONF                 0x00000400
-/*------------------------------------------------------------------*/
-/*  dtmf definitions                                                */
-/*------------------------------------------------------------------*/
-#define DTMF_LISTEN_START     1
-#define DTMF_LISTEN_STOP      2
-#define DTMF_DIGITS_SEND      3
-#define DTMF_SUCCESS          0
-#define DTMF_INCORRECT_DIGIT  1
-#define DTMF_UNKNOWN_REQUEST  2
-/*------------------------------------------------------------------*/
-/*  line interconnect definitions                                   */
-/*------------------------------------------------------------------*/
-#define LI_GET_SUPPORTED_SERVICES       0
-#define LI_REQ_CONNECT                  1
-#define LI_REQ_DISCONNECT               2
-#define LI_IND_CONNECT_ACTIVE           1
-#define LI_IND_DISCONNECT               2
-#define LI_FLAG_CONFERENCE_A_B          ((dword) 0x00000001L)
-#define LI_FLAG_CONFERENCE_B_A          ((dword) 0x00000002L)
-#define LI_FLAG_MONITOR_A               ((dword) 0x00000004L)
-#define LI_FLAG_MONITOR_B               ((dword) 0x00000008L)
-#define LI_FLAG_ANNOUNCEMENT_A          ((dword) 0x00000010L)
-#define LI_FLAG_ANNOUNCEMENT_B          ((dword) 0x00000020L)
-#define LI_FLAG_MIX_A                   ((dword) 0x00000040L)
-#define LI_FLAG_MIX_B                   ((dword) 0x00000080L)
-#define LI_CONFERENCING_SUPPORTED       ((dword) 0x00000001L)
-#define LI_MONITORING_SUPPORTED         ((dword) 0x00000002L)
-#define LI_ANNOUNCEMENTS_SUPPORTED      ((dword) 0x00000004L)
-#define LI_MIXING_SUPPORTED             ((dword) 0x00000008L)
-#define LI_CROSS_CONTROLLER_SUPPORTED   ((dword) 0x00000010L)
-#define LI2_GET_SUPPORTED_SERVICES      0
-#define LI2_REQ_CONNECT                 1
-#define LI2_REQ_DISCONNECT              2
-#define LI2_IND_CONNECT_ACTIVE          1
-#define LI2_IND_DISCONNECT              2
-#define LI2_FLAG_INTERCONNECT_A_B       ((dword) 0x00000001L)
-#define LI2_FLAG_INTERCONNECT_B_A       ((dword) 0x00000002L)
-#define LI2_FLAG_MONITOR_B              ((dword) 0x00000004L)
-#define LI2_FLAG_MIX_B                  ((dword) 0x00000008L)
-#define LI2_FLAG_MONITOR_X              ((dword) 0x00000010L)
-#define LI2_FLAG_MIX_X                  ((dword) 0x00000020L)
-#define LI2_FLAG_LOOP_B                 ((dword) 0x00000040L)
-#define LI2_FLAG_LOOP_PC                ((dword) 0x00000080L)
-#define LI2_FLAG_LOOP_X                 ((dword) 0x00000100L)
-#define LI2_CROSS_CONTROLLER_SUPPORTED  ((dword) 0x00000001L)
-#define LI2_ASYMMETRIC_SUPPORTED        ((dword) 0x00000002L)
-#define LI2_MONITORING_SUPPORTED        ((dword) 0x00000004L)
-#define LI2_MIXING_SUPPORTED            ((dword) 0x00000008L)
-#define LI2_REMOTE_MONITORING_SUPPORTED ((dword) 0x00000010L)
-#define LI2_REMOTE_MIXING_SUPPORTED     ((dword) 0x00000020L)
-#define LI2_B_LOOPING_SUPPORTED         ((dword) 0x00000040L)
-#define LI2_PC_LOOPING_SUPPORTED        ((dword) 0x00000080L)
-#define LI2_X_LOOPING_SUPPORTED         ((dword) 0x00000100L)
-/*------------------------------------------------------------------*/
-/* echo canceller definitions                                       */
-/*------------------------------------------------------------------*/
-#define EC_GET_SUPPORTED_SERVICES            0
-#define EC_ENABLE_OPERATION                  1
-#define EC_DISABLE_OPERATION                 2
-#define EC_ENABLE_NON_LINEAR_PROCESSING      0x0001
-#define EC_DO_NOT_REQUIRE_REVERSALS          0x0002
-#define EC_DETECT_DISABLE_TONE               0x0004
-#define EC_ENABLE_ADAPTIVE_PREDELAY          0x0008
-#define EC_NON_LINEAR_PROCESSING_SUPPORTED   0x0001
-#define EC_BYPASS_ON_ANY_2100HZ_SUPPORTED    0x0002
-#define EC_BYPASS_ON_REV_2100HZ_SUPPORTED    0x0004
-#define EC_ADAPTIVE_PREDELAY_SUPPORTED       0x0008
-#define EC_BYPASS_INDICATION                 1
-#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ   1
-#define EC_BYPASS_DUE_TO_REVERSED_2100HZ     2
-#define EC_BYPASS_RELEASED                   3
-/*------------------------------------------------------------------*/
-/* function prototypes                                              */
-/*------------------------------------------------------------------*/
-/*------------------------------------------------------------------*/
-#endif /* _INC_CAPI20 */
diff --git a/drivers/isdn/hardware/eicon/capidtmf.c b/drivers/isdn/hardware/eicon/capidtmf.c
deleted file mode 100644 (file)
index e3f7784..0000000
+++ /dev/null
@@ -1,685 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "platform.h"
-
-
-
-
-
-
-
-
-
-#include "capidtmf.h"
-
-/* #define TRACE_ */
-
-#define FILE_ "CAPIDTMF.C"
-
-/*---------------------------------------------------------------------------*/
-
-
-#define trace(a)
-
-
-
-/*---------------------------------------------------------------------------*/
-
-static short capidtmf_expand_table_alaw[0x0100] =
-{
-       -5504,   5504,   -344,    344, -22016,  22016,  -1376,   1376,
-       -2752,   2752,    -88,     88, -11008,  11008,   -688,    688,
-       -7552,   7552,   -472,    472, -30208,  30208,  -1888,   1888,
-       -3776,   3776,   -216,    216, -15104,  15104,   -944,    944,
-       -4480,   4480,   -280,    280, -17920,  17920,  -1120,   1120,
-       -2240,   2240,    -24,     24,  -8960,   8960,   -560,    560,
-       -6528,   6528,   -408,    408, -26112,  26112,  -1632,   1632,
-       -3264,   3264,   -152,    152, -13056,  13056,   -816,    816,
-       -6016,   6016,   -376,    376, -24064,  24064,  -1504,   1504,
-       -3008,   3008,   -120,    120, -12032,  12032,   -752,    752,
-       -8064,   8064,   -504,    504, -32256,  32256,  -2016,   2016,
-       -4032,   4032,   -248,    248, -16128,  16128,  -1008,   1008,
-       -4992,   4992,   -312,    312, -19968,  19968,  -1248,   1248,
-       -2496,   2496,    -56,     56,  -9984,   9984,   -624,    624,
-       -7040,   7040,   -440,    440, -28160,  28160,  -1760,   1760,
-       -3520,   3520,   -184,    184, -14080,  14080,   -880,    880,
-       -5248,   5248,   -328,    328, -20992,  20992,  -1312,   1312,
-       -2624,   2624,    -72,     72, -10496,  10496,   -656,    656,
-       -7296,   7296,   -456,    456, -29184,  29184,  -1824,   1824,
-       -3648,   3648,   -200,    200, -14592,  14592,   -912,    912,
-       -4224,   4224,   -264,    264, -16896,  16896,  -1056,   1056,
-       -2112,   2112,     -8,      8,  -8448,   8448,   -528,    528,
-       -6272,   6272,   -392,    392, -25088,  25088,  -1568,   1568,
-       -3136,   3136,   -136,    136, -12544,  12544,   -784,    784,
-       -5760,   5760,   -360,    360, -23040,  23040,  -1440,   1440,
-       -2880,   2880,   -104,    104, -11520,  11520,   -720,    720,
-       -7808,   7808,   -488,    488, -31232,  31232,  -1952,   1952,
-       -3904,   3904,   -232,    232, -15616,  15616,   -976,    976,
-       -4736,   4736,   -296,    296, -18944,  18944,  -1184,   1184,
-       -2368,   2368,    -40,     40,  -9472,   9472,   -592,    592,
-       -6784,   6784,   -424,    424, -27136,  27136,  -1696,   1696,
-       -3392,   3392,   -168,    168, -13568,  13568,   -848,    848
-};
-
-static short capidtmf_expand_table_ulaw[0x0100] =
-{
-       -32124,  32124,  -1884,   1884,  -7932,   7932,   -372,    372,
-       -15996,  15996,   -876,    876,  -3900,   3900,   -120,    120,
-       -23932,  23932,  -1372,   1372,  -5884,   5884,   -244,    244,
-       -11900,  11900,   -620,    620,  -2876,   2876,    -56,     56,
-       -28028,  28028,  -1628,   1628,  -6908,   6908,   -308,    308,
-       -13948,  13948,   -748,    748,  -3388,   3388,    -88,     88,
-       -19836,  19836,  -1116,   1116,  -4860,   4860,   -180,    180,
-       -9852,   9852,   -492,    492,  -2364,   2364,    -24,     24,
-       -30076,  30076,  -1756,   1756,  -7420,   7420,   -340,    340,
-       -14972,  14972,   -812,    812,  -3644,   3644,   -104,    104,
-       -21884,  21884,  -1244,   1244,  -5372,   5372,   -212,    212,
-       -10876,  10876,   -556,    556,  -2620,   2620,    -40,     40,
-       -25980,  25980,  -1500,   1500,  -6396,   6396,   -276,    276,
-       -12924,  12924,   -684,    684,  -3132,   3132,    -72,     72,
-       -17788,  17788,   -988,    988,  -4348,   4348,   -148,    148,
-       -8828,   8828,   -428,    428,  -2108,   2108,     -8,      8,
-       -31100,  31100,  -1820,   1820,  -7676,   7676,   -356,    356,
-       -15484,  15484,   -844,    844,  -3772,   3772,   -112,    112,
-       -22908,  22908,  -1308,   1308,  -5628,   5628,   -228,    228,
-       -11388,  11388,   -588,    588,  -2748,   2748,    -48,     48,
-       -27004,  27004,  -1564,   1564,  -6652,   6652,   -292,    292,
-       -13436,  13436,   -716,    716,  -3260,   3260,    -80,     80,
-       -18812,  18812,  -1052,   1052,  -4604,   4604,   -164,    164,
-       -9340,   9340,   -460,    460,  -2236,   2236,    -16,     16,
-       -29052,  29052,  -1692,   1692,  -7164,   7164,   -324,    324,
-       -14460,  14460,   -780,    780,  -3516,   3516,    -96,     96,
-       -20860,  20860,  -1180,   1180,  -5116,   5116,   -196,    196,
-       -10364,  10364,   -524,    524,  -2492,   2492,    -32,     32,
-       -24956,  24956,  -1436,   1436,  -6140,   6140,   -260,    260,
-       -12412,  12412,   -652,    652,  -3004,   3004,    -64,     64,
-       -16764,  16764,   -924,    924,  -4092,   4092,   -132,    132,
-       -8316,   8316,   -396,    396,  -1980,   1980,      0,      0
-};
-
-
-/*---------------------------------------------------------------------------*/
-
-static short capidtmf_recv_window_function[CAPIDTMF_RECV_ACCUMULATE_CYCLES] =
-{
-       -500L,   -999L,  -1499L,  -1998L,  -2496L,  -2994L,  -3491L,  -3988L,
-       -4483L,  -4978L,  -5471L,  -5963L,  -6454L,  -6943L,  -7431L,  -7917L,
-       -8401L,  -8883L,  -9363L,  -9840L, -10316L, -10789L, -11259L, -11727L,
-       -12193L, -12655L, -13115L, -13571L, -14024L, -14474L, -14921L, -15364L,
-       -15804L, -16240L, -16672L, -17100L, -17524L, -17944L, -18360L, -18772L,
-       -19180L, -19583L, -19981L, -20375L, -20764L, -21148L, -21527L, -21901L,
-       -22270L, -22634L, -22993L, -23346L, -23694L, -24037L, -24374L, -24705L,
-       -25030L, -25350L, -25664L, -25971L, -26273L, -26568L, -26858L, -27141L,
-       -27418L, -27688L, -27952L, -28210L, -28461L, -28705L, -28943L, -29174L,
-       -29398L, -29615L, -29826L, -30029L, -30226L, -30415L, -30598L, -30773L,
-       -30941L, -31102L, -31256L, -31402L, -31541L, -31673L, -31797L, -31914L,
-       -32024L, -32126L, -32221L, -32308L, -32388L, -32460L, -32524L, -32581L,
-       -32631L, -32673L, -32707L, -32734L, -32753L, -32764L, -32768L, -32764L,
-       -32753L, -32734L, -32707L, -32673L, -32631L, -32581L, -32524L, -32460L,
-       -32388L, -32308L, -32221L, -32126L, -32024L, -31914L, -31797L, -31673L,
-       -31541L, -31402L, -31256L, -31102L, -30941L, -30773L, -30598L, -30415L,
-       -30226L, -30029L, -29826L, -29615L, -29398L, -29174L, -28943L, -28705L,
-       -28461L, -28210L, -27952L, -27688L, -27418L, -27141L, -26858L, -26568L,
-       -26273L, -25971L, -25664L, -25350L, -25030L, -24705L, -24374L, -24037L,
-       -23694L, -23346L, -22993L, -22634L, -22270L, -21901L, -21527L, -21148L,
-       -20764L, -20375L, -19981L, -19583L, -19180L, -18772L, -18360L, -17944L,
-       -17524L, -17100L, -16672L, -16240L, -15804L, -15364L, -14921L, -14474L,
-       -14024L, -13571L, -13115L, -12655L, -12193L, -11727L, -11259L, -10789L,
-       -10316L,  -9840L,  -9363L,  -8883L,  -8401L,  -7917L,  -7431L,  -6943L,
-       -6454L,  -5963L,  -5471L,  -4978L,  -4483L,  -3988L,  -3491L,  -2994L,
-       -2496L,  -1998L,  -1499L,   -999L,   -500L,
-};
-
-static byte capidtmf_leading_zeroes_table[0x100] =
-{
-       8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
-       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-#define capidtmf_byte_leading_zeroes(b)  (capidtmf_leading_zeroes_table[(BYTE)(b)])
-#define capidtmf_word_leading_zeroes(w)  (((w) & 0xff00) ? capidtmf_leading_zeroes_table[(w) >> 8] : 8 + capidtmf_leading_zeroes_table[(w)])
-#define capidtmf_dword_leading_zeroes(d)  (((d) & 0xffff0000L) ?    (((d) & 0xff000000L) ? capidtmf_leading_zeroes_table[(d) >> 24] : 8 + capidtmf_leading_zeroes_table[(d) >> 16]) :    (((d) & 0xff00) ? 16 + capidtmf_leading_zeroes_table[(d) >> 8] : 24 + capidtmf_leading_zeroes_table[(d)]))
-
-
-/*---------------------------------------------------------------------------*/
-
-
-static void capidtmf_goertzel_loop(long *buffer, long *coeffs, short *sample, long count)
-{
-       int i, j;
-       long c, d, q0, q1, q2;
-
-       for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1; i++)
-       {
-               q1 = buffer[i];
-               q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
-               d = coeffs[i] >> 1;
-               c = d << 1;
-               if (c >= 0)
-               {
-                       for (j = 0; j < count; j++)
-                       {
-                               q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15);
-                               q2 = q1;
-                               q1 = q0;
-                       }
-               }
-               else
-               {
-                       c = -c;
-                       d = -d;
-                       for (j = 0; j < count; j++)
-                       {
-                               q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15));
-                               q2 = q1;
-                               q1 = q0;
-                       }
-               }
-               buffer[i] = q1;
-               buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
-       }
-       q1 = buffer[i];
-       q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
-       c = (coeffs[i] >> 1) << 1;
-       if (c >= 0)
-       {
-               for (j = 0; j < count; j++)
-               {
-                       q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15);
-                       q2 = q1;
-                       q1 = q0;
-                       c -= CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
-               }
-       }
-       else
-       {
-               c = -c;
-               for (j = 0; j < count; j++)
-               {
-                       q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15));
-                       q2 = q1;
-                       q1 = q0;
-                       c += CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
-               }
-       }
-       coeffs[i] = c;
-       buffer[i] = q1;
-       buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
-}
-
-
-static void capidtmf_goertzel_result(long *buffer, long *coeffs)
-{
-       int i;
-       long d, e, q1, q2, lo, mid, hi;
-       dword k;
-
-       for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
-       {
-               q1 = buffer[i];
-               q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
-               d = coeffs[i] >> 1;
-               if (d >= 0)
-                       d = ((d << 1) * (-q1 >> 16)) + (((dword)(((dword) d) * ((dword)(-q1 & 0xffff)))) >> 15);
-               else
-                       d = ((-d << 1) * (-q1 >> 16)) + (((dword)(((dword) -d) * ((dword)(-q1 & 0xffff)))) >> 15);
-               e = (q2 >= 0) ? q2 : -q2;
-               if (d >= 0)
-               {
-                       k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
-                       lo = k & 0xffff;
-                       mid = k >> 16;
-                       k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
-                       mid += k & 0xffff;
-                       hi = k >> 16;
-                       k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
-                       mid += k & 0xffff;
-                       hi += k >> 16;
-                       hi += ((dword)(d >> 16)) * ((dword)(e >> 16));
-               }
-               else
-               {
-                       d = -d;
-                       k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
-                       lo = -((long)(k & 0xffff));
-                       mid = -((long)(k >> 16));
-                       k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
-                       mid -= k & 0xffff;
-                       hi = -((long)(k >> 16));
-                       k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
-                       mid -= k & 0xffff;
-                       hi -= k >> 16;
-                       hi -= ((dword)(d >> 16)) * ((dword)(e >> 16));
-               }
-               if (q2 < 0)
-               {
-                       lo = -lo;
-                       mid = -mid;
-                       hi = -hi;
-               }
-               d = (q1 >= 0) ? q1 : -q1;
-               k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
-               lo += k & 0xffff;
-               mid += k >> 16;
-               k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
-               mid += (k & 0xffff) << 1;
-               hi += (k >> 16) << 1;
-               hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
-               d = (q2 >= 0) ? q2 : -q2;
-               k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
-               lo += k & 0xffff;
-               mid += k >> 16;
-               k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
-               mid += (k & 0xffff) << 1;
-               hi += (k >> 16) << 1;
-               hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
-               mid += lo >> 16;
-               hi += mid >> 16;
-               buffer[i] = (lo & 0xffff) | (mid << 16);
-               buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = hi;
-       }
-}
-
-
-/*---------------------------------------------------------------------------*/
-
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_697     0
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_770     1
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_852     2
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_941     3
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1209    4
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1336    5
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1477    6
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1633    7
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_635     8
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1010    9
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1140    10
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1272    11
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1405    12
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1555    13
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1715    14
-#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1875    15
-
-#define CAPIDTMF_RECV_GUARD_SNR_DONTCARE      0xc000
-#define CAPIDTMF_RECV_NO_DIGIT                0xff
-#define CAPIDTMF_RECV_TIME_GRANULARITY        (CAPIDTMF_RECV_ACCUMULATE_CYCLES + 1)
-
-#define CAPIDTMF_RECV_INDICATION_DIGIT        0x0001
-
-static long capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
-{
-       0xda97L * 2,  /* 697 Hz (Low group 697 Hz) */
-       0xd299L * 2,  /* 770 Hz (Low group 770 Hz) */
-       0xc8cbL * 2,  /* 852 Hz (Low group 852 Hz) */
-       0xbd36L * 2,  /* 941 Hz (Low group 941 Hz) */
-       0x9501L * 2,  /* 1209 Hz (High group 1209 Hz) */
-       0x7f89L * 2,  /* 1336 Hz (High group 1336 Hz) */
-       0x6639L * 2,  /* 1477 Hz (High group 1477 Hz) */
-       0x48c6L * 2,  /* 1633 Hz (High group 1633 Hz) */
-       0xe14cL * 2,  /* 630 Hz (Lower guard of low group 631 Hz) */
-       0xb2e0L * 2,  /* 1015 Hz (Upper guard of low group 1039 Hz) */
-       0xa1a0L * 2,  /* 1130 Hz (Lower guard of high group 1140 Hz) */
-       0x8a87L * 2,  /* 1272 Hz (Guard between 1209 Hz and 1336 Hz: 1271 Hz) */
-       0x7353L * 2,  /* 1405 Hz (2nd harmonics of 697 Hz and guard between 1336 Hz and 1477 Hz: 1405 Hz) */
-       0x583bL * 2,  /* 1552 Hz (2nd harmonics of 770 Hz and guard between 1477 Hz and 1633 Hz: 1553 Hz) */
-       0x37d8L * 2,  /* 1720 Hz (2nd harmonics of 852 Hz and upper guard of high group: 1715 Hz) */
-       0x0000L * 2   /* 100-630 Hz (fundamentals) */
-};
-
-
-static word capidtmf_recv_guard_snr_low_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
-{
-       14,                                    /* Low group peak versus 697 Hz */
-       14,                                    /* Low group peak versus 770 Hz */
-       16,                                    /* Low group peak versus 852 Hz */
-       16,                                    /* Low group peak versus 941 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1209 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1336 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1477 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1633 Hz */
-       14,                                    /* Low group peak versus 635 Hz */
-       16,                                    /* Low group peak versus 1010 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1140 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1272 Hz */
-       DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 8,  /* Low group peak versus 1405 Hz */
-       DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4,  /* Low group peak versus 1555 Hz */
-       DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4,  /* Low group peak versus 1715 Hz */
-       12                                     /* Low group peak versus 100-630 Hz */
-};
-
-
-static word capidtmf_recv_guard_snr_high_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
-{
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 697 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 770 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 852 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 941 Hz */
-       20,                                    /* High group peak versus 1209 Hz */
-       20,                                    /* High group peak versus 1336 Hz */
-       20,                                    /* High group peak versus 1477 Hz */
-       20,                                    /* High group peak versus 1633 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 635 Hz */
-       CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 1010 Hz */
-       16,                                    /* High group peak versus 1140 Hz */
-       4,                                     /* High group peak versus 1272 Hz */
-       6,                                     /* High group peak versus 1405 Hz */
-       8,                                     /* High group peak versus 1555 Hz */
-       16,                                    /* High group peak versus 1715 Hz */
-       12                                     /* High group peak versus 100-630 Hz */
-};
-
-
-/*---------------------------------------------------------------------------*/
-
-static void capidtmf_recv_init(t_capidtmf_state *p_state)
-{
-       p_state->recv.min_gap_duration = 1;
-       p_state->recv.min_digit_duration = 1;
-
-       p_state->recv.cycle_counter = 0;
-       p_state->recv.current_digit_on_time = 0;
-       p_state->recv.current_digit_off_time = 0;
-       p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
-
-       p_state->recv.digit_write_pos = 0;
-       p_state->recv.digit_read_pos = 0;
-       p_state->recv.indication_state = 0;
-       p_state->recv.indication_state_ack = 0;
-       p_state->recv.state = CAPIDTMF_RECV_STATE_IDLE;
-}
-
-
-void capidtmf_recv_enable(t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration)
-{
-       p_state->recv.indication_state_ack &= CAPIDTMF_RECV_INDICATION_DIGIT;
-       p_state->recv.min_digit_duration = (word)(((((dword) min_digit_duration) * 8) +
-                                                  ((dword)(CAPIDTMF_RECV_TIME_GRANULARITY / 2))) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
-       if (p_state->recv.min_digit_duration <= 1)
-               p_state->recv.min_digit_duration = 1;
-       else
-               (p_state->recv.min_digit_duration)--;
-       p_state->recv.min_gap_duration =
-               (word)((((dword) min_gap_duration) * 8) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
-       if (p_state->recv.min_gap_duration <= 1)
-               p_state->recv.min_gap_duration = 1;
-       else
-               (p_state->recv.min_gap_duration)--;
-       p_state->recv.state |= CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
-}
-
-
-void capidtmf_recv_disable(t_capidtmf_state *p_state)
-{
-       p_state->recv.state &= ~CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
-       if (p_state->recv.state == CAPIDTMF_RECV_STATE_IDLE)
-               capidtmf_recv_init(p_state);
-       else
-       {
-               p_state->recv.cycle_counter = 0;
-               p_state->recv.current_digit_on_time = 0;
-               p_state->recv.current_digit_off_time = 0;
-               p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
-       }
-}
-
-
-word capidtmf_recv_indication(t_capidtmf_state *p_state, byte *buffer)
-{
-       word i, j, k, flags;
-
-       flags = p_state->recv.indication_state ^ p_state->recv.indication_state_ack;
-       p_state->recv.indication_state_ack ^= flags & CAPIDTMF_RECV_INDICATION_DIGIT;
-       if (p_state->recv.digit_write_pos != p_state->recv.digit_read_pos)
-       {
-               i = 0;
-               k = p_state->recv.digit_write_pos;
-               j = p_state->recv.digit_read_pos;
-               do
-               {
-                       buffer[i++] = p_state->recv.digit_buffer[j];
-                       j = (j == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ? 0 : j + 1;
-               } while (j != k);
-               p_state->recv.digit_read_pos = k;
-               return (i);
-       }
-       p_state->recv.indication_state_ack ^= flags;
-       return (0);
-}
-
-
-#define CAPIDTMF_RECV_WINDOWED_SAMPLES  32
-
-void capidtmf_recv_block(t_capidtmf_state *p_state, byte *buffer, word length)
-{
-       byte result_digit;
-       word sample_number, cycle_counter, n, i;
-       word low_peak, high_peak;
-       dword lo, hi;
-       byte   *p;
-       short *q;
-       byte goertzel_result_buffer[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
-       short windowed_sample_buffer[CAPIDTMF_RECV_WINDOWED_SAMPLES];
-
-
-       if (p_state->recv.state & CAPIDTMF_RECV_STATE_DTMF_ACTIVE)
-       {
-               cycle_counter = p_state->recv.cycle_counter;
-               sample_number = 0;
-               while (sample_number < length)
-               {
-                       if (cycle_counter < CAPIDTMF_RECV_ACCUMULATE_CYCLES)
-                       {
-                               if (cycle_counter == 0)
-                               {
-                                       for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
-                                       {
-                                               p_state->recv.goertzel_buffer[0][i] = 0;
-                                               p_state->recv.goertzel_buffer[1][i] = 0;
-                                       }
-                               }
-                               n = CAPIDTMF_RECV_ACCUMULATE_CYCLES - cycle_counter;
-                               if (n > length - sample_number)
-                                       n = length - sample_number;
-                               if (n > CAPIDTMF_RECV_WINDOWED_SAMPLES)
-                                       n = CAPIDTMF_RECV_WINDOWED_SAMPLES;
-                               p = buffer + sample_number;
-                               q = capidtmf_recv_window_function + cycle_counter;
-                               if (p_state->ulaw)
-                               {
-                                       for (i = 0; i < n; i++)
-                                       {
-                                               windowed_sample_buffer[i] =
-                                                       (short)((capidtmf_expand_table_ulaw[p[i]] * ((long)(q[i]))) >> 15);
-                                       }
-                               }
-                               else
-                               {
-                                       for (i = 0; i < n; i++)
-                                       {
-                                               windowed_sample_buffer[i] =
-                                                       (short)((capidtmf_expand_table_alaw[p[i]] * ((long)(q[i]))) >> 15);
-                                       }
-                               }
-                               capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1] = CAPIDTMF_RECV_FUNDAMENTAL_OFFSET;
-                               capidtmf_goertzel_loop(p_state->recv.goertzel_buffer[0],
-                                                      capidtmf_recv_goertzel_coef_table, windowed_sample_buffer, n);
-                               cycle_counter += n;
-                               sample_number += n;
-                       }
-                       else
-                       {
-                               capidtmf_goertzel_result(p_state->recv.goertzel_buffer[0],
-                                                        capidtmf_recv_goertzel_coef_table);
-                               for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
-                               {
-                                       lo = (dword)(p_state->recv.goertzel_buffer[0][i]);
-                                       hi = (dword)(p_state->recv.goertzel_buffer[1][i]);
-                                       if (hi != 0)
-                                       {
-                                               n = capidtmf_dword_leading_zeroes(hi);
-                                               hi = (hi << n) | (lo >> (32 - n));
-                                       }
-                                       else
-                                       {
-                                               n = capidtmf_dword_leading_zeroes(lo);
-                                               hi = lo << n;
-                                               n += 32;
-                                       }
-                                       n = 195 - 3 * n;
-                                       if (hi >= 0xcb300000L)
-                                               n += 2;
-                                       else if (hi >= 0xa1450000L)
-                                               n++;
-                                       goertzel_result_buffer[i] = (byte) n;
-                               }
-                               low_peak = DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT;
-                               result_digit = CAPIDTMF_RECV_NO_DIGIT;
-                               for (i = 0; i < CAPIDTMF_LOW_GROUP_FREQUENCIES; i++)
-                               {
-                                       if (goertzel_result_buffer[i] > low_peak)
-                                       {
-                                               low_peak = goertzel_result_buffer[i];
-                                               result_digit = (byte) i;
-                                       }
-                               }
-                               high_peak = DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT;
-                               n = CAPIDTMF_RECV_NO_DIGIT;
-                               for (i = CAPIDTMF_LOW_GROUP_FREQUENCIES; i < CAPIDTMF_RECV_BASE_FREQUENCY_COUNT; i++)
-                               {
-                                       if (goertzel_result_buffer[i] > high_peak)
-                                       {
-                                               high_peak = goertzel_result_buffer[i];
-                                               n = (i - CAPIDTMF_LOW_GROUP_FREQUENCIES) << 2;
-                                       }
-                               }
-                               result_digit |= (byte) n;
-                               if (low_peak + DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT < high_peak)
-                                       result_digit = CAPIDTMF_RECV_NO_DIGIT;
-                               if (high_peak + DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT < low_peak)
-                                       result_digit = CAPIDTMF_RECV_NO_DIGIT;
-                               n = 0;
-                               for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
-                               {
-                                       if ((((short)(low_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_low_table[i])) < 0)
-                                           || (((short)(high_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_high_table[i])) < 0))
-                                       {
-                                               n++;
-                                       }
-                               }
-                               if (n != 2)
-                                       result_digit = CAPIDTMF_RECV_NO_DIGIT;
-
-                               if (result_digit == CAPIDTMF_RECV_NO_DIGIT)
-                               {
-                                       if (p_state->recv.current_digit_on_time != 0)
-                                       {
-                                               if (++(p_state->recv.current_digit_off_time) >= p_state->recv.min_gap_duration)
-                                               {
-                                                       p_state->recv.current_digit_on_time = 0;
-                                                       p_state->recv.current_digit_off_time = 0;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               if (p_state->recv.current_digit_off_time != 0)
-                                                       (p_state->recv.current_digit_off_time)--;
-                                       }
-                               }
-                               else
-                               {
-                                       if ((p_state->recv.current_digit_on_time == 0)
-                                           && (p_state->recv.current_digit_off_time != 0))
-                                       {
-                                               (p_state->recv.current_digit_off_time)--;
-                                       }
-                                       else
-                                       {
-                                               n = p_state->recv.current_digit_off_time;
-                                               if ((p_state->recv.current_digit_on_time != 0)
-                                                   && (result_digit != p_state->recv.current_digit_value))
-                                               {
-                                                       p_state->recv.current_digit_on_time = 0;
-                                                       n = 0;
-                                               }
-                                               p_state->recv.current_digit_value = result_digit;
-                                               p_state->recv.current_digit_off_time = 0;
-                                               if (p_state->recv.current_digit_on_time != 0xffff)
-                                               {
-                                                       p_state->recv.current_digit_on_time += n + 1;
-                                                       if (p_state->recv.current_digit_on_time >= p_state->recv.min_digit_duration)
-                                                       {
-                                                               p_state->recv.current_digit_on_time = 0xffff;
-                                                               i = (p_state->recv.digit_write_pos == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ?
-                                                                       0 : p_state->recv.digit_write_pos + 1;
-                                                               if (i == p_state->recv.digit_read_pos)
-                                                               {
-                                                                       trace(dprintf("%s,%d: Receive digit overrun",
-                                                                                     (char *)(FILE_), __LINE__));
-                                                               }
-                                                               else
-                                                               {
-                                                                       p_state->recv.digit_buffer[p_state->recv.digit_write_pos] = result_digit;
-                                                                       p_state->recv.digit_write_pos = i;
-                                                                       p_state->recv.indication_state =
-                                                                               (p_state->recv.indication_state & ~CAPIDTMF_RECV_INDICATION_DIGIT) |
-                                                                               (~p_state->recv.indication_state_ack & CAPIDTMF_RECV_INDICATION_DIGIT);
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                               cycle_counter = 0;
-                               sample_number++;
-                       }
-               }
-               p_state->recv.cycle_counter = cycle_counter;
-       }
-}
-
-
-void capidtmf_init(t_capidtmf_state *p_state, byte ulaw)
-{
-       p_state->ulaw = ulaw;
-       capidtmf_recv_init(p_state);
-}
-
-
-/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/capidtmf.h b/drivers/isdn/hardware/eicon/capidtmf.h
deleted file mode 100644 (file)
index 0a9cf59..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef CAPIDTMF_H_
-#define CAPIDTMF_H_
-/*---------------------------------------------------------------------------*/
-/*---------------------------------------------------------------------------*/
-#define CAPIDTMF_TONE_GROUP_COUNT            2
-#define CAPIDTMF_LOW_GROUP_FREQUENCIES       4
-#define CAPIDTMF_HIGH_GROUP_FREQUENCIES      4
-#define DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT     50      /* -52 dBm */
-#define DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT    50      /* -52 dBm */
-#define DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT  10      /* dB */
-#define DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT  10      /* dB */
-#define DSPDTMF_RX_HARMONICS_SEL_DEFAULT       12      /* dB */
-#define CAPIDTMF_RECV_BASE_FREQUENCY_COUNT   (CAPIDTMF_LOW_GROUP_FREQUENCIES + CAPIDTMF_HIGH_GROUP_FREQUENCIES)
-#define CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT  8
-#define CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT  (CAPIDTMF_RECV_BASE_FREQUENCY_COUNT + CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT)
-#define CAPIDTMF_RECV_POSITIVE_COEFF_COUNT   16
-#define CAPIDTMF_RECV_NEGATIVE_COEFF_COUNT   (CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - CAPIDTMF_RECV_POSITIVE_COEFF_COUNT)
-#define CAPIDTMF_RECV_ACCUMULATE_CYCLES      205
-#define CAPIDTMF_RECV_FUNDAMENTAL_OFFSET     (0xff35L * 2)
-#define CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT  (0x0028L * 2)
-#define CAPIDTMF_RECV_DIGIT_BUFFER_SIZE      32
-#define CAPIDTMF_RECV_STATE_IDLE             0x00
-#define CAPIDTMF_RECV_STATE_DTMF_ACTIVE      0x01
-typedef struct tag_capidtmf_recv_state
-{
-       byte digit_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE];
-       word digit_write_pos;
-       word digit_read_pos;
-       word indication_state;
-       word indication_state_ack;
-       long goertzel_buffer[2][CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
-       word min_gap_duration;
-       word min_digit_duration;
-       word cycle_counter;
-       word current_digit_on_time;
-       word current_digit_off_time;
-       byte current_digit_value;
-       byte state;
-} t_capidtmf_recv_state;
-typedef struct tag_capidtmf_state
-{
-       byte ulaw;
-       t_capidtmf_recv_state recv;
-} t_capidtmf_state;
-word capidtmf_recv_indication(t_capidtmf_state *p_state, byte *buffer);
-void capidtmf_recv_block(t_capidtmf_state *p_state, byte *buffer, word length);
-void capidtmf_init(t_capidtmf_state *p_state, byte ulaw);
-void capidtmf_recv_enable(t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration);
-void capidtmf_recv_disable(t_capidtmf_state *p_state);
-#define capidtmf_indication(p_state, buffer)  (((p_state)->recv.indication_state != (p_state)->recv.indication_state_ack) ? capidtmf_recv_indication(p_state, buffer) : 0)
-#define capidtmf_recv_process_block(p_state, buffer, length)  { if ((p_state)->recv.state != CAPIDTMF_RECV_STATE_IDLE) capidtmf_recv_block(p_state, buffer, length); }
-/*---------------------------------------------------------------------------*/
-/*---------------------------------------------------------------------------*/
-#endif
diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c
deleted file mode 100644 (file)
index 7a0bdbd..0000000
+++ /dev/null
@@ -1,1219 +0,0 @@
-/* $Id: capifunc.c,v 1.61.4.7 2005/02/11 19:40:25 armin Exp $
- *
- * ISDN interface module for Eicon active cards DIVA.
- * CAPI Interface common functions
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include "platform.h"
-#include "os_capi.h"
-#include "di_defs.h"
-#include "capi20.h"
-#include "divacapi.h"
-#include "divasync.h"
-#include "capifunc.h"
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-DIVA_CAPI_ADAPTER *adapter = (DIVA_CAPI_ADAPTER *) NULL;
-APPL *application = (APPL *) NULL;
-byte max_appl = MAX_APPL;
-byte max_adapter = 0;
-static CAPI_MSG *mapped_msg = (CAPI_MSG *) NULL;
-
-byte UnMapController(byte);
-char DRIVERRELEASE_CAPI[32];
-
-extern void AutomaticLaw(DIVA_CAPI_ADAPTER *);
-extern void callback(ENTITY *);
-extern word api_remove_start(void);
-extern word CapiRelease(word);
-extern word CapiRegister(word);
-extern word api_put(APPL *, CAPI_MSG *);
-
-static diva_os_spin_lock_t api_lock;
-
-static LIST_HEAD(cards);
-
-static dword notify_handle;
-static void DIRequest(ENTITY *e);
-static DESCRIPTOR MAdapter;
-static DESCRIPTOR DAdapter;
-static byte ControllerMap[MAX_DESCRIPTORS + 1];
-
-
-static void diva_register_appl(struct capi_ctr *, __u16,
-                              capi_register_params *);
-static void diva_release_appl(struct capi_ctr *, __u16);
-static char *diva_procinfo(struct capi_ctr *);
-static u16 diva_send_message(struct capi_ctr *,
-                            diva_os_message_buffer_s *);
-extern void diva_os_set_controller_struct(struct capi_ctr *);
-
-extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
-
-/*
- * debug
- */
-static void no_printf(unsigned char *, ...);
-#include "debuglib.c"
-static void xlog(char *x, ...)
-{
-#ifndef DIVA_NO_DEBUGLIB
-       va_list ap;
-       if (myDriverDebugHandle.dbgMask & DL_XLOG) {
-               va_start(ap, x);
-               if (myDriverDebugHandle.dbg_irq) {
-                       myDriverDebugHandle.dbg_irq(myDriverDebugHandle.id,
-                                                   DLI_XLOG, x, ap);
-               } else if (myDriverDebugHandle.dbg_old) {
-                       myDriverDebugHandle.dbg_old(myDriverDebugHandle.id,
-                                                   x, ap);
-               }
-               va_end(ap);
-       }
-#endif
-}
-
-/*
- * info for proc
- */
-static char *diva_procinfo(struct capi_ctr *ctrl)
-{
-       return (ctrl->serial);
-}
-
-/*
- * stop debugging
- */
-static void stop_dbg(void)
-{
-       DbgDeregister();
-       memset(&MAdapter, 0, sizeof(MAdapter));
-       dprintf = no_printf;
-}
-
-/*
- * dummy debug function
- */
-static void no_printf(unsigned char *x, ...)
-{
-}
-
-/*
- * Controller mapping
- */
-byte MapController(byte Controller)
-{
-       byte i;
-       byte MappedController = 0;
-       byte ctrl = Controller & 0x7f;  /* mask external controller bit off */
-
-       for (i = 1; i < max_adapter + 1; i++) {
-               if (ctrl == ControllerMap[i]) {
-                       MappedController = (byte) i;
-                       break;
-               }
-       }
-       if (i > max_adapter) {
-               ControllerMap[0] = ctrl;
-               MappedController = 0;
-       }
-       return (MappedController | (Controller & 0x80));        /* put back external controller bit */
-}
-
-/*
- * Controller unmapping
- */
-byte UnMapController(byte MappedController)
-{
-       byte Controller;
-       byte ctrl = MappedController & 0x7f;    /* mask external controller bit off */
-
-       if (ctrl <= max_adapter) {
-               Controller = ControllerMap[ctrl];
-       } else {
-               Controller = 0;
-       }
-
-       return (Controller | (MappedController & 0x80));        /* put back external controller bit */
-}
-
-/*
- * find a new free id
- */
-static int find_free_id(void)
-{
-       int num = 0;
-       DIVA_CAPI_ADAPTER *a;
-
-       while (num < MAX_DESCRIPTORS) {
-               a = &adapter[num];
-               if (!a->Id)
-                       break;
-               num++;
-       }
-       return (num + 1);
-}
-
-/*
- * find a card structure by controller number
- */
-static diva_card *find_card_by_ctrl(word controller)
-{
-       struct list_head *tmp;
-       diva_card *card;
-
-       list_for_each(tmp, &cards) {
-               card = list_entry(tmp, diva_card, list);
-               if (ControllerMap[card->Id] == controller) {
-                       if (card->remove_in_progress)
-                               card = NULL;
-                       return (card);
-               }
-       }
-       return (diva_card *) 0;
-}
-
-/*
- * Buffer RX/TX
- */
-void *TransmitBufferSet(APPL *appl, dword ref)
-{
-       appl->xbuffer_used[ref] = true;
-       DBG_PRV1(("%d:xbuf_used(%d)", appl->Id, ref + 1))
-               return (void *)(long)ref;
-}
-
-void *TransmitBufferGet(APPL *appl, void *p)
-{
-       if (appl->xbuffer_internal[(dword)(long)p])
-               return appl->xbuffer_internal[(dword)(long)p];
-
-       return appl->xbuffer_ptr[(dword)(long)p];
-}
-
-void TransmitBufferFree(APPL *appl, void *p)
-{
-       appl->xbuffer_used[(dword)(long)p] = false;
-       DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword)(long)p) + 1))
-               }
-
-void *ReceiveBufferGet(APPL *appl, int Num)
-{
-       return &appl->ReceiveBuffer[Num * appl->MaxDataLength];
-}
-
-/*
- * api_remove_start/complete for cleanup
- */
-void api_remove_complete(void)
-{
-       DBG_PRV1(("api_remove_complete"))
-               }
-
-/*
- * main function called by message.c
- */
-void sendf(APPL *appl, word command, dword Id, word Number, byte *format, ...)
-{
-       word i, j;
-       word length = 12, dlength = 0;
-       byte *write;
-       CAPI_MSG msg;
-       byte *string = NULL;
-       va_list ap;
-       diva_os_message_buffer_s *dmb;
-       diva_card *card = NULL;
-       dword tmp;
-
-       if (!appl)
-               return;
-
-       DBG_PRV1(("sendf(a=%d,cmd=%x,format=%s)",
-                 appl->Id, command, (byte *) format))
-
-               PUT_WORD(&msg.header.appl_id, appl->Id);
-       PUT_WORD(&msg.header.command, command);
-       if ((byte) (command >> 8) == 0x82)
-               Number = appl->Number++;
-       PUT_WORD(&msg.header.number, Number);
-
-       PUT_DWORD(&msg.header.controller, Id);
-       write = (byte *)&msg;
-       write += 12;
-
-       va_start(ap, format);
-       for (i = 0; format[i]; i++) {
-               switch (format[i]) {
-               case 'b':
-                       tmp = va_arg(ap, dword);
-                       *(byte *) write = (byte) (tmp & 0xff);
-                       write += 1;
-                       length += 1;
-                       break;
-               case 'w':
-                       tmp = va_arg(ap, dword);
-                       PUT_WORD(write, (tmp & 0xffff));
-                       write += 2;
-                       length += 2;
-                       break;
-               case 'd':
-                       tmp = va_arg(ap, dword);
-                       PUT_DWORD(write, tmp);
-                       write += 4;
-                       length += 4;
-                       break;
-               case 's':
-               case 'S':
-                       string = va_arg(ap, byte *);
-                       length += string[0] + 1;
-                       for (j = 0; j <= string[0]; j++)
-                               *write++ = string[j];
-                       break;
-               }
-       }
-       va_end(ap);
-
-       PUT_WORD(&msg.header.length, length);
-       msg.header.controller = UnMapController(msg.header.controller);
-
-       if (command == _DATA_B3_I)
-               dlength = GET_WORD(
-                       ((byte *)&msg.info.data_b3_ind.Data_Length));
-
-       if (!(dmb = diva_os_alloc_message_buffer(length + dlength,
-                                                (void **) &write))) {
-               DBG_ERR(("sendf: alloc_message_buffer failed, incoming msg dropped."))
-                       return;
-       }
-
-       /* copy msg header to sk_buff */
-       memcpy(write, (byte *)&msg, length);
-
-       /* if DATA_B3_IND, copy data too */
-       if (command == _DATA_B3_I) {
-               dword data = GET_DWORD(&msg.info.data_b3_ind.Data);
-               memcpy(write + length, (void *)(long)data, dlength);
-       }
-
-#ifndef DIVA_NO_DEBUGLIB
-       if (myDriverDebugHandle.dbgMask & DL_XLOG) {
-               switch (command) {
-               default:
-                       xlog("\x00\x02", &msg, 0x81, length);
-                       break;
-               case _DATA_B3_R | CONFIRM:
-                       if (myDriverDebugHandle.dbgMask & DL_BLK)
-                               xlog("\x00\x02", &msg, 0x81, length);
-                       break;
-               case _DATA_B3_I:
-                       if (myDriverDebugHandle.dbgMask & DL_BLK) {
-                               xlog("\x00\x02", &msg, 0x81, length);
-                               for (i = 0; i < dlength; i += 256) {
-                                       DBG_BLK((((char *)(long)GET_DWORD(&msg.info.data_b3_ind.Data)) + i,
-                                                ((dlength - i) < 256) ? (dlength - i) : 256))
-                                               if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
-                                                       break; /* not more if not explicitly requested */
-                               }
-                       }
-                       break;
-               }
-       }
-#endif
-
-       /* find the card structure for this controller */
-       if (!(card = find_card_by_ctrl(write[8] & 0x7f))) {
-               DBG_ERR(("sendf - controller %d not found, incoming msg dropped",
-                        write[8] & 0x7f))
-                       diva_os_free_message_buffer(dmb);
-               return;
-       }
-       /* send capi msg to capi layer */
-       capi_ctr_handle_message(&card->capi_ctrl, appl->Id, dmb);
-}
-
-/*
- * cleanup adapter
- */
-static void clean_adapter(int id, struct list_head *free_mem_q)
-{
-       DIVA_CAPI_ADAPTER *a;
-       int i, k;
-
-       a = &adapter[id];
-       k = li_total_channels - a->li_channels;
-       if (k == 0) {
-               if (li_config_table) {
-                       list_add((struct list_head *)li_config_table, free_mem_q);
-                       li_config_table = NULL;
-               }
-       } else {
-               if (a->li_base < k) {
-                       memmove(&li_config_table[a->li_base],
-                               &li_config_table[a->li_base + a->li_channels],
-                               (k - a->li_base) * sizeof(LI_CONFIG));
-                       for (i = 0; i < k; i++) {
-                               memmove(&li_config_table[i].flag_table[a->li_base],
-                                       &li_config_table[i].flag_table[a->li_base + a->li_channels],
-                                       k - a->li_base);
-                               memmove(&li_config_table[i].
-                                       coef_table[a->li_base],
-                                       &li_config_table[i].coef_table[a->li_base + a->li_channels],
-                                       k - a->li_base);
-                       }
-               }
-       }
-       li_total_channels = k;
-       for (i = id; i < max_adapter; i++) {
-               if (adapter[i].request)
-                       adapter[i].li_base -= a->li_channels;
-       }
-       if (a->plci)
-               list_add((struct list_head *)a->plci, free_mem_q);
-
-       memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER));
-       while ((max_adapter != 0) && !adapter[max_adapter - 1].request)
-               max_adapter--;
-}
-
-/*
- * remove a card, but ensures consistent state of LI tables
- * in the time adapter is removed
- */
-static void divacapi_remove_card(DESCRIPTOR *d)
-{
-       diva_card *card = NULL;
-       diva_os_spin_lock_magic_t old_irql;
-       LIST_HEAD(free_mem_q);
-       struct list_head *link;
-       struct list_head *tmp;
-
-       /*
-        * Set "remove in progress flag".
-        * Ensures that there is no call from sendf to CAPI in
-        * the time CAPI controller is about to be removed.
-        */
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
-       list_for_each(tmp, &cards) {
-               card = list_entry(tmp, diva_card, list);
-               if (card->d.request == d->request) {
-                       card->remove_in_progress = 1;
-                       list_del(tmp);
-                       break;
-               }
-       }
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
-
-       if (card) {
-               /*
-                * Detach CAPI. Sendf cannot call to CAPI any more.
-                * After detach no call to send_message() is done too.
-                */
-               detach_capi_ctr(&card->capi_ctrl);
-
-               /*
-                * Now get API lock (to ensure stable state of LI tables)
-                * and update the adapter map/LI table.
-                */
-               diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
-
-               clean_adapter(card->Id - 1, &free_mem_q);
-               DBG_TRC(("DelAdapterMap (%d) -> (%d)",
-                        ControllerMap[card->Id], card->Id))
-                       ControllerMap[card->Id] = 0;
-               DBG_TRC(("adapter remove, max_adapter=%d",
-                        max_adapter));
-               diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
-
-               /* After releasing the lock, we can free the memory */
-               diva_os_free(0, card);
-       }
-
-       /* free queued memory areas */
-       list_for_each_safe(link, tmp, &free_mem_q) {
-               list_del(link);
-               diva_os_free(0, link);
-       }
-}
-
-/*
- * remove cards
- */
-static void divacapi_remove_cards(void)
-{
-       DESCRIPTOR d;
-       struct list_head *tmp;
-       diva_card *card;
-       diva_os_spin_lock_magic_t old_irql;
-
-rescan:
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards");
-       list_for_each(tmp, &cards) {
-               card = list_entry(tmp, diva_card, list);
-               diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
-               d.request = card->d.request;
-               divacapi_remove_card(&d);
-               goto rescan;
-       }
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
-}
-
-/*
- * sync_callback
- */
-static void sync_callback(ENTITY *e)
-{
-       diva_os_spin_lock_magic_t old_irql;
-
-       DBG_TRC(("cb:Id=%x,Rc=%x,Ind=%x", e->Id, e->Rc, e->Ind))
-
-               diva_os_enter_spin_lock(&api_lock, &old_irql, "sync_callback");
-       callback(e);
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "sync_callback");
-}
-
-/*
- * add a new card
- */
-static int diva_add_card(DESCRIPTOR *d)
-{
-       int k = 0, i = 0;
-       diva_os_spin_lock_magic_t old_irql;
-       diva_card *card = NULL;
-       struct capi_ctr *ctrl = NULL;
-       DIVA_CAPI_ADAPTER *a = NULL;
-       IDI_SYNC_REQ sync_req;
-       char serial[16];
-       void *mem_to_free;
-       LI_CONFIG *new_li_config_table;
-       int j;
-
-       if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) {
-               DBG_ERR(("diva_add_card: failed to allocate card struct."))
-                       return (0);
-       }
-       memset((char *) card, 0x00, sizeof(diva_card));
-       memcpy(&card->d, d, sizeof(DESCRIPTOR));
-       sync_req.GetName.Req = 0;
-       sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
-       card->d.request((ENTITY *)&sync_req);
-       strlcpy(card->name, sync_req.GetName.name, sizeof(card->name));
-       ctrl = &card->capi_ctrl;
-       strcpy(ctrl->name, card->name);
-       ctrl->register_appl = diva_register_appl;
-       ctrl->release_appl = diva_release_appl;
-       ctrl->send_message = diva_send_message;
-       ctrl->procinfo = diva_procinfo;
-       ctrl->driverdata = card;
-       diva_os_set_controller_struct(ctrl);
-
-       if (attach_capi_ctr(ctrl)) {
-               DBG_ERR(("diva_add_card: failed to attach controller."))
-                       diva_os_free(0, card);
-               return (0);
-       }
-
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "find id");
-       card->Id = find_free_id();
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "find id");
-
-       strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu));
-       ctrl->version.majorversion = 2;
-       ctrl->version.minorversion = 0;
-       ctrl->version.majormanuversion = DRRELMAJOR;
-       ctrl->version.minormanuversion = DRRELMINOR;
-       sync_req.GetSerial.Req = 0;
-       sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
-       sync_req.GetSerial.serial = 0;
-       card->d.request((ENTITY *)&sync_req);
-       if ((i = ((sync_req.GetSerial.serial & 0xff000000) >> 24))) {
-               sprintf(serial, "%ld-%d",
-                       sync_req.GetSerial.serial & 0x00ffffff, i + 1);
-       } else {
-               sprintf(serial, "%ld", sync_req.GetSerial.serial);
-       }
-       serial[CAPI_SERIAL_LEN - 1] = 0;
-       strlcpy(ctrl->serial, serial, sizeof(ctrl->serial));
-
-       a = &adapter[card->Id - 1];
-       card->adapter = a;
-       a->os_card = card;
-       ControllerMap[card->Id] = (byte) (ctrl->cnr);
-
-       DBG_TRC(("AddAdapterMap (%d) -> (%d)", ctrl->cnr, card->Id))
-
-               sync_req.xdi_capi_prms.Req = 0;
-       sync_req.xdi_capi_prms.Rc = IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS;
-       sync_req.xdi_capi_prms.info.structure_length =
-               sizeof(diva_xdi_get_capi_parameters_t);
-       card->d.request((ENTITY *)&sync_req);
-       a->flag_dynamic_l1_down =
-               sync_req.xdi_capi_prms.info.flag_dynamic_l1_down;
-       a->group_optimization_enabled =
-               sync_req.xdi_capi_prms.info.group_optimization_enabled;
-       a->request = DIRequest; /* card->d.request; */
-       a->max_plci = card->d.channels + 30;
-       a->max_listen = (card->d.channels > 2) ? 8 : 2;
-       if (!
-           (a->plci =
-            (PLCI *) diva_os_malloc(0, sizeof(PLCI) * a->max_plci))) {
-               DBG_ERR(("diva_add_card: failed alloc plci struct."))
-                       memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
-               return (0);
-       }
-       memset(a->plci, 0, sizeof(PLCI) * a->max_plci);
-
-       for (k = 0; k < a->max_plci; k++) {
-               a->Id = (byte) card->Id;
-               a->plci[k].Sig.callback = sync_callback;
-               a->plci[k].Sig.XNum = 1;
-               a->plci[k].Sig.X = a->plci[k].XData;
-               a->plci[k].Sig.user[0] = (word) (card->Id - 1);
-               a->plci[k].Sig.user[1] = (word) k;
-               a->plci[k].NL.callback = sync_callback;
-               a->plci[k].NL.XNum = 1;
-               a->plci[k].NL.X = a->plci[k].XData;
-               a->plci[k].NL.user[0] = (word) ((card->Id - 1) | 0x8000);
-               a->plci[k].NL.user[1] = (word) k;
-               a->plci[k].adapter = a;
-       }
-
-       a->profile.Number = card->Id;
-       a->profile.Channels = card->d.channels;
-       if (card->d.features & DI_FAX3) {
-               a->profile.Global_Options = 0x71;
-               if (card->d.features & DI_CODEC)
-                       a->profile.Global_Options |= 0x6;
-#if IMPLEMENT_DTMF
-               a->profile.Global_Options |= 0x8;
-#endif                         /* IMPLEMENT_DTMF */
-               a->profile.Global_Options |= 0x80; /* Line Interconnect */
-#if IMPLEMENT_ECHO_CANCELLER
-               a->profile.Global_Options |= 0x100;
-#endif                         /* IMPLEMENT_ECHO_CANCELLER */
-               a->profile.B1_Protocols = 0xdf;
-               a->profile.B2_Protocols = 0x1fdb;
-               a->profile.B3_Protocols = 0xb7;
-               a->manufacturer_features = MANUFACTURER_FEATURE_HARDDTMF;
-       } else {
-               a->profile.Global_Options = 0x71;
-               if (card->d.features & DI_CODEC)
-                       a->profile.Global_Options |= 0x2;
-               a->profile.B1_Protocols = 0x43;
-               a->profile.B2_Protocols = 0x1f0f;
-               a->profile.B3_Protocols = 0x07;
-               a->manufacturer_features = 0;
-       }
-
-       a->li_pri = (a->profile.Channels > 2);
-       a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI;
-       a->li_base = 0;
-       for (i = 0; &adapter[i] != a; i++) {
-               if (adapter[i].request)
-                       a->li_base = adapter[i].li_base + adapter[i].li_channels;
-       }
-       k = li_total_channels + a->li_channels;
-       new_li_config_table =
-               (LI_CONFIG *) diva_os_malloc(0, ((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * k) * ((k + 3) & ~3));
-       if (new_li_config_table == NULL) {
-               DBG_ERR(("diva_add_card: failed alloc li_config table."))
-                       memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
-               return (0);
-       }
-
-       /* Prevent access to line interconnect table in process update */
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "add card");
-
-       j = 0;
-       for (i = 0; i < k; i++) {
-               if ((i >= a->li_base) && (i < a->li_base + a->li_channels))
-                       memset(&new_li_config_table[i], 0, sizeof(LI_CONFIG));
-               else
-                       memcpy(&new_li_config_table[i], &li_config_table[j], sizeof(LI_CONFIG));
-               new_li_config_table[i].flag_table =
-                       ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i) * ((k + 3) & ~3));
-               new_li_config_table[i].coef_table =
-                       ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i + 1) * ((k + 3) & ~3));
-               if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) {
-                       new_li_config_table[i].adapter = a;
-                       memset(&new_li_config_table[i].flag_table[0], 0, k);
-                       memset(&new_li_config_table[i].coef_table[0], 0, k);
-               } else {
-                       if (a->li_base != 0) {
-                               memcpy(&new_li_config_table[i].flag_table[0],
-                                      &li_config_table[j].flag_table[0],
-                                      a->li_base);
-                               memcpy(&new_li_config_table[i].coef_table[0],
-                                      &li_config_table[j].coef_table[0],
-                                      a->li_base);
-                       }
-                       memset(&new_li_config_table[i].flag_table[a->li_base], 0, a->li_channels);
-                       memset(&new_li_config_table[i].coef_table[a->li_base], 0, a->li_channels);
-                       if (a->li_base + a->li_channels < k) {
-                               memcpy(&new_li_config_table[i].flag_table[a->li_base +
-                                                                         a->li_channels],
-                                      &li_config_table[j].flag_table[a->li_base],
-                                      k - (a->li_base + a->li_channels));
-                               memcpy(&new_li_config_table[i].coef_table[a->li_base +
-                                                                         a->li_channels],
-                                      &li_config_table[j].coef_table[a->li_base],
-                                      k - (a->li_base + a->li_channels));
-                       }
-                       j++;
-               }
-       }
-       li_total_channels = k;
-
-       mem_to_free = li_config_table;
-
-       li_config_table = new_li_config_table;
-       for (i = card->Id; i < max_adapter; i++) {
-               if (adapter[i].request)
-                       adapter[i].li_base += a->li_channels;
-       }
-
-       if (a == &adapter[max_adapter])
-               max_adapter++;
-
-       list_add(&(card->list), &cards);
-       AutomaticLaw(a);
-
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "add card");
-
-       if (mem_to_free) {
-               diva_os_free(0, mem_to_free);
-       }
-
-       i = 0;
-       while (i++ < 30) {
-               if (a->automatic_law > 3)
-                       break;
-               diva_os_sleep(10);
-       }
-
-       /* profile information */
-       PUT_WORD(&ctrl->profile.nbchannel, card->d.channels);
-       ctrl->profile.goptions = a->profile.Global_Options;
-       ctrl->profile.support1 = a->profile.B1_Protocols;
-       ctrl->profile.support2 = a->profile.B2_Protocols;
-       ctrl->profile.support3 = a->profile.B3_Protocols;
-       /* manufacturer profile information */
-       ctrl->profile.manu[0] = a->man_profile.private_options;
-       ctrl->profile.manu[1] = a->man_profile.rtp_primary_payloads;
-       ctrl->profile.manu[2] = a->man_profile.rtp_additional_payloads;
-       ctrl->profile.manu[3] = 0;
-       ctrl->profile.manu[4] = 0;
-
-       capi_ctr_ready(ctrl);
-
-       DBG_TRC(("adapter added, max_adapter=%d", max_adapter));
-       return (1);
-}
-
-/*
- *  register appl
- */
-static void diva_register_appl(struct capi_ctr *ctrl, __u16 appl,
-                              capi_register_params *rp)
-{
-       APPL *this;
-       word bnum, xnum;
-       int i = 0;
-       unsigned char *p;
-       void *DataNCCI, *DataFlags, *ReceiveBuffer, *xbuffer_used;
-       void **xbuffer_ptr, **xbuffer_internal;
-       diva_os_spin_lock_magic_t old_irql;
-       unsigned int mem_len;
-       int nconn = rp->level3cnt;
-
-
-       if (diva_os_in_irq()) {
-               DBG_ERR(("CAPI_REGISTER - in irq context !"))
-                       return;
-       }
-
-       DBG_TRC(("application register Id=%d", appl))
-
-               if (appl > MAX_APPL) {
-                       DBG_ERR(("CAPI_REGISTER - appl.Id exceeds MAX_APPL"))
-                               return;
-               }
-
-       if (nconn <= 0)
-               nconn = ctrl->profile.nbchannel * -nconn;
-
-       if (nconn == 0)
-               nconn = ctrl->profile.nbchannel;
-
-       DBG_LOG(("CAPI_REGISTER - Id = %d", appl))
-               DBG_LOG(("  MaxLogicalConnections = %d(%d)", nconn, rp->level3cnt))
-               DBG_LOG(("  MaxBDataBuffers       = %d", rp->datablkcnt))
-               DBG_LOG(("  MaxBDataLength        = %d", rp->datablklen))
-
-               if (nconn < 1 ||
-                   nconn > 255 ||
-                   rp->datablklen < 80 ||
-                   rp->datablklen > 2150 || rp->datablkcnt > 255) {
-                       DBG_ERR(("CAPI_REGISTER - invalid parameters"))
-                               return;
-               }
-
-       if (application[appl - 1].Id == appl) {
-               DBG_LOG(("CAPI_REGISTER - appl already registered"))
-                       return; /* appl already registered */
-       }
-
-       /* alloc memory */
-
-       bnum = nconn * rp->datablkcnt;
-       xnum = nconn * MAX_DATA_B3;
-
-       mem_len  = bnum * sizeof(word);         /* DataNCCI */
-       mem_len += bnum * sizeof(word);         /* DataFlags */
-       mem_len += bnum * rp->datablklen;       /* ReceiveBuffer */
-       mem_len += xnum;                        /* xbuffer_used */
-       mem_len += xnum * sizeof(void *);       /* xbuffer_ptr */
-       mem_len += xnum * sizeof(void *);       /* xbuffer_internal */
-       mem_len += xnum * rp->datablklen;       /* xbuffer_ptr[xnum] */
-
-       DBG_LOG(("  Allocated Memory      = %d", mem_len))
-               if (!(p = diva_os_malloc(0, mem_len))) {
-                       DBG_ERR(("CAPI_REGISTER - memory allocation failed"))
-                               return;
-               }
-       memset(p, 0, mem_len);
-
-       DataNCCI = (void *)p;
-       p += bnum * sizeof(word);
-       DataFlags = (void *)p;
-       p += bnum * sizeof(word);
-       ReceiveBuffer = (void *)p;
-       p += bnum * rp->datablklen;
-       xbuffer_used = (void *)p;
-       p += xnum;
-       xbuffer_ptr = (void **)p;
-       p += xnum * sizeof(void *);
-       xbuffer_internal = (void **)p;
-       p += xnum * sizeof(void *);
-       for (i = 0; i < xnum; i++) {
-               xbuffer_ptr[i] = (void *)p;
-               p += rp->datablklen;
-       }
-
-       /* initialize application data */
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "register_appl");
-
-       this = &application[appl - 1];
-       memset(this, 0, sizeof(APPL));
-
-       this->Id = appl;
-
-       for (i = 0; i < max_adapter; i++) {
-               adapter[i].CIP_Mask[appl - 1] = 0;
-       }
-
-       this->queue_size = 1000;
-
-       this->MaxNCCI = (byte) nconn;
-       this->MaxNCCIData = (byte) rp->datablkcnt;
-       this->MaxBuffer = bnum;
-       this->MaxDataLength = rp->datablklen;
-
-       this->DataNCCI = DataNCCI;
-       this->DataFlags = DataFlags;
-       this->ReceiveBuffer = ReceiveBuffer;
-       this->xbuffer_used = xbuffer_used;
-       this->xbuffer_ptr = xbuffer_ptr;
-       this->xbuffer_internal = xbuffer_internal;
-       for (i = 0; i < xnum; i++) {
-               this->xbuffer_ptr[i] = xbuffer_ptr[i];
-       }
-
-       CapiRegister(this->Id);
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "register_appl");
-
-}
-
-/*
- *  release appl
- */
-static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       APPL *this = &application[appl - 1];
-       void *mem_to_free = NULL;
-
-       DBG_TRC(("application %d(%d) cleanup", this->Id, appl))
-
-               if (diva_os_in_irq()) {
-                       DBG_ERR(("CAPI_RELEASE - in irq context !"))
-                               return;
-               }
-
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl");
-       if (this->Id) {
-               CapiRelease(this->Id);
-               mem_to_free = this->DataNCCI;
-               this->DataNCCI = NULL;
-               this->Id = 0;
-       }
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl");
-
-       if (mem_to_free)
-               diva_os_free(0, mem_to_free);
-
-}
-
-/*
- *  send message
- */
-static u16 diva_send_message(struct capi_ctr *ctrl,
-                            diva_os_message_buffer_s *dmb)
-{
-       int i = 0;
-       word ret = 0;
-       diva_os_spin_lock_magic_t old_irql;
-       CAPI_MSG *msg = (CAPI_MSG *) DIVA_MESSAGE_BUFFER_DATA(dmb);
-       APPL *this = &application[GET_WORD(&msg->header.appl_id) - 1];
-       diva_card *card = ctrl->driverdata;
-       __u32 length = DIVA_MESSAGE_BUFFER_LEN(dmb);
-       word clength = GET_WORD(&msg->header.length);
-       word command = GET_WORD(&msg->header.command);
-       u16 retval = CAPI_NOERROR;
-
-       if (diva_os_in_irq()) {
-               DBG_ERR(("CAPI_SEND_MSG - in irq context !"))
-                       return CAPI_REGOSRESOURCEERR;
-       }
-       DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command))
-
-               if (card->remove_in_progress) {
-                       DBG_ERR(("CAPI_SEND_MSG - remove in progress!"))
-                               return CAPI_REGOSRESOURCEERR;
-               }
-
-       diva_os_enter_spin_lock(&api_lock, &old_irql, "send message");
-
-       if (!this->Id) {
-               diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
-               return CAPI_ILLAPPNR;
-       }
-
-       /* patch controller number */
-       msg->header.controller = ControllerMap[card->Id]
-               | (msg->header.controller & 0x80);      /* preserve external controller bit */
-
-       switch (command) {
-       default:
-               xlog("\x00\x02", msg, 0x80, clength);
-               break;
-
-       case _DATA_B3_I | RESPONSE:
-#ifndef DIVA_NO_DEBUGLIB
-               if (myDriverDebugHandle.dbgMask & DL_BLK)
-                       xlog("\x00\x02", msg, 0x80, clength);
-#endif
-               break;
-
-       case _DATA_B3_R:
-#ifndef DIVA_NO_DEBUGLIB
-               if (myDriverDebugHandle.dbgMask & DL_BLK)
-                       xlog("\x00\x02", msg, 0x80, clength);
-#endif
-
-               if (clength == 24)
-                       clength = 22;   /* workaround for PPcom bug */
-               /* header is always 22      */
-               if (GET_WORD(&msg->info.data_b3_req.Data_Length) >
-                   this->MaxDataLength
-                   || GET_WORD(&msg->info.data_b3_req.Data_Length) >
-                   (length - clength)) {
-                       DBG_ERR(("Write - invalid message size"))
-                               retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
-                       goto write_end;
-               }
-
-               for (i = 0; i < (MAX_DATA_B3 * this->MaxNCCI)
-                            && this->xbuffer_used[i]; i++);
-               if (i == (MAX_DATA_B3 * this->MaxNCCI)) {
-                       DBG_ERR(("Write - too many data pending"))
-                               retval = CAPI_SENDQUEUEFULL;
-                       goto write_end;
-               }
-               msg->info.data_b3_req.Data = i;
-
-               this->xbuffer_internal[i] = NULL;
-               memcpy(this->xbuffer_ptr[i], &((__u8 *) msg)[clength],
-                      GET_WORD(&msg->info.data_b3_req.Data_Length));
-
-#ifndef DIVA_NO_DEBUGLIB
-               if ((myDriverDebugHandle.dbgMask & DL_BLK)
-                   && (myDriverDebugHandle.dbgMask & DL_XLOG)) {
-                       int j;
-                       for (j = 0; j <
-                                    GET_WORD(&msg->info.data_b3_req.Data_Length);
-                            j += 256) {
-                               DBG_BLK((((char *) this->xbuffer_ptr[i]) + j,
-                                        ((GET_WORD(&msg->info.data_b3_req.Data_Length) - j) <
-                                         256) ? (GET_WORD(&msg->info.data_b3_req.Data_Length) - j) : 256))
-                                       if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
-                                               break;  /* not more if not explicitly requested */
-                       }
-               }
-#endif
-               break;
-       }
-
-       memcpy(mapped_msg, msg, (__u32) clength);
-       mapped_msg->header.controller = MapController(mapped_msg->header.controller);
-       mapped_msg->header.length = clength;
-       mapped_msg->header.command = command;
-       mapped_msg->header.number = GET_WORD(&msg->header.number);
-
-       ret = api_put(this, mapped_msg);
-       switch (ret) {
-       case 0:
-               break;
-       case _BAD_MSG:
-               DBG_ERR(("Write - bad message"))
-                       retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
-               break;
-       case _QUEUE_FULL:
-               DBG_ERR(("Write - queue full"))
-                       retval = CAPI_SENDQUEUEFULL;
-               break;
-       default:
-               DBG_ERR(("Write - api_put returned unknown error"))
-                       retval = CAPI_UNKNOWNNOTPAR;
-               break;
-       }
-
-write_end:
-       diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
-       if (retval == CAPI_NOERROR)
-               diva_os_free_message_buffer(dmb);
-       return retval;
-}
-
-
-/*
- * cards request function
- */
-static void DIRequest(ENTITY *e)
-{
-       DIVA_CAPI_ADAPTER *a = &(adapter[(byte) e->user[0]]);
-       diva_card *os_card = (diva_card *) a->os_card;
-
-       if (e->Req && (a->FlowControlIdTable[e->ReqCh] == e->Id)) {
-               a->FlowControlSkipTable[e->ReqCh] = 1;
-       }
-
-       (*(os_card->d.request)) (e);
-}
-
-/*
- * callback function from didd
- */
-static void didd_callback(void *context, DESCRIPTOR *adapter, int removal)
-{
-       if (adapter->type == IDI_DADAPTER) {
-               DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
-               return;
-       } else if (adapter->type == IDI_DIMAINT) {
-               if (removal) {
-                       stop_dbg();
-               } else {
-                       memcpy(&MAdapter, adapter, sizeof(MAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
-               }
-       } else if ((adapter->type > 0) && (adapter->type < 16)) {       /* IDI Adapter */
-               if (removal) {
-                       divacapi_remove_card(adapter);
-               } else {
-                       diva_add_card(adapter);
-               }
-       }
-       return;
-}
-
-/*
- * connect to didd
- */
-static int divacapi_connect_didd(void)
-{
-       int x = 0;
-       int dadapter = 0;
-       IDI_SYNC_REQ req;
-       DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
-
-       DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
-
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DIMAINT) {        /* MAINT found */
-                       memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
-                       break;
-               }
-       }
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
-                       dadapter = 1;
-                       memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       req.didd_notify.e.Req = 0;
-                       req.didd_notify.e.Rc =
-                               IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
-                       req.didd_notify.info.callback = (void *)didd_callback;
-                       req.didd_notify.info.context = NULL;
-                       DAdapter.request((ENTITY *)&req);
-                       if (req.didd_notify.e.Rc != 0xff) {
-                               stop_dbg();
-                               return (0);
-                       }
-                       notify_handle = req.didd_notify.info.handle;
-               }
-               else if ((DIDD_Table[x].type > 0) && (DIDD_Table[x].type < 16)) {       /* IDI Adapter found */
-                       diva_add_card(&DIDD_Table[x]);
-               }
-       }
-
-       if (!dadapter) {
-               stop_dbg();
-       }
-
-       return (dadapter);
-}
-
-/*
- * diconnect from didd
- */
-static void divacapi_disconnect_didd(void)
-{
-       IDI_SYNC_REQ req;
-
-       stop_dbg();
-
-       req.didd_notify.e.Req = 0;
-       req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
-       req.didd_notify.info.handle = notify_handle;
-       DAdapter.request((ENTITY *)&req);
-}
-
-/*
- * we do not provide date/time here,
- * the application should do this.
- */
-int fax_head_line_time(char *buffer)
-{
-       return (0);
-}
-
-/*
- * init (alloc) main structures
- */
-static int __init init_main_structs(void)
-{
-       if (!(mapped_msg = (CAPI_MSG *) diva_os_malloc(0, MAX_MSG_SIZE))) {
-               DBG_ERR(("init: failed alloc mapped_msg."))
-                       return 0;
-       }
-
-       if (!(adapter = diva_os_malloc(0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS))) {
-               DBG_ERR(("init: failed alloc adapter struct."))
-                       diva_os_free(0, mapped_msg);
-               return 0;
-       }
-       memset(adapter, 0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS);
-
-       if (!(application = diva_os_malloc(0, sizeof(APPL) * MAX_APPL))) {
-               DBG_ERR(("init: failed alloc application struct."))
-                       diva_os_free(0, mapped_msg);
-               diva_os_free(0, adapter);
-               return 0;
-       }
-       memset(application, 0, sizeof(APPL) * MAX_APPL);
-
-       return (1);
-}
-
-/*
- * remove (free) main structures
- */
-static void remove_main_structs(void)
-{
-       if (application)
-               diva_os_free(0, application);
-       if (adapter)
-               diva_os_free(0, adapter);
-       if (mapped_msg)
-               diva_os_free(0, mapped_msg);
-}
-
-/*
- * api_remove_start
- */
-static void do_api_remove_start(void)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       int ret = 1, count = 100;
-
-       do {
-               diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start");
-               ret = api_remove_start();
-               diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start");
-
-               diva_os_sleep(10);
-       } while (ret && count--);
-
-       if (ret)
-               DBG_ERR(("could not remove signaling ID's"))
-                       }
-
-/*
- * init
- */
-int __init init_capifunc(void)
-{
-       diva_os_initialize_spin_lock(&api_lock, "capifunc");
-       memset(ControllerMap, 0, MAX_DESCRIPTORS + 1);
-       max_adapter = 0;
-
-
-       if (!init_main_structs()) {
-               DBG_ERR(("init: failed to init main structs."))
-                       diva_os_destroy_spin_lock(&api_lock, "capifunc");
-               return (0);
-       }
-
-       if (!divacapi_connect_didd()) {
-               DBG_ERR(("init: failed to connect to DIDD."))
-                       do_api_remove_start();
-               divacapi_remove_cards();
-               remove_main_structs();
-               diva_os_destroy_spin_lock(&api_lock, "capifunc");
-               return (0);
-       }
-
-       return (1);
-}
-
-/*
- * finit
- */
-void __exit finit_capifunc(void)
-{
-       do_api_remove_start();
-       divacapi_disconnect_didd();
-       divacapi_remove_cards();
-       remove_main_structs();
-       diva_os_destroy_spin_lock(&api_lock, "capifunc");
-}
diff --git a/drivers/isdn/hardware/eicon/capifunc.h b/drivers/isdn/hardware/eicon/capifunc.h
deleted file mode 100644 (file)
index e96c45b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $
- *
- * ISDN interface module for Eicon active cards DIVA.
- * CAPI Interface common functions
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#ifndef __CAPIFUNC_H__
-#define __CAPIFUNC_H__
-
-#define DRRELMAJOR  2
-#define DRRELMINOR  0
-#define DRRELEXTRA  ""
-
-#define M_COMPANY "Eicon Networks"
-
-extern char DRIVERRELEASE_CAPI[];
-
-typedef struct _diva_card {
-       struct list_head list;
-       int remove_in_progress;
-       int Id;
-       struct capi_ctr capi_ctrl;
-       DIVA_CAPI_ADAPTER *adapter;
-       DESCRIPTOR d;
-       char name[32];
-} diva_card;
-
-/*
- * prototypes
- */
-int init_capifunc(void);
-void finit_capifunc(void);
-
-#endif /* __CAPIFUNC_H__ */
diff --git a/drivers/isdn/hardware/eicon/capimain.c b/drivers/isdn/hardware/eicon/capimain.c
deleted file mode 100644 (file)
index f9244dc..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/* $Id: capimain.c,v 1.24 2003/09/09 06:51:05 schindler Exp $
- *
- * ISDN interface module for Eicon active cards DIVA.
- * CAPI Interface
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/uaccess.h>
-#include <linux/seq_file.h>
-#include <linux/skbuff.h>
-
-#include "os_capi.h"
-
-#include "platform.h"
-#include "di_defs.h"
-#include "capi20.h"
-#include "divacapi.h"
-#include "cp_vers.h"
-#include "capifunc.h"
-
-static char *main_revision = "$Revision: 1.24 $";
-static char *DRIVERNAME =
-       "Eicon DIVA - CAPI Interface driver (http://www.melware.net)";
-static char *DRIVERLNAME = "divacapi";
-
-MODULE_DESCRIPTION("CAPI driver for Eicon DIVA cards");
-MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
-MODULE_SUPPORTED_DEVICE("CAPI and DIVA card drivers");
-MODULE_LICENSE("GPL");
-
-/*
- * get revision number from revision string
- */
-static char *getrev(const char *revision)
-{
-       char *rev;
-       char *p;
-       if ((p = strchr(revision, ':'))) {
-               rev = p + 2;
-               p = strchr(rev, '$');
-               *--p = 0;
-       } else
-               rev = "1.0";
-       return rev;
-
-}
-
-/*
- * alloc a message buffer
- */
-diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size,
-                                                      void **data_buf)
-{
-       diva_os_message_buffer_s *dmb = alloc_skb(size, GFP_ATOMIC);
-       if (dmb) {
-               *data_buf = skb_put(dmb, size);
-       }
-       return (dmb);
-}
-
-/*
- * free a message buffer
- */
-void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb)
-{
-       kfree_skb(dmb);
-}
-
-/*
- * proc function for controller info
- */
-static int diva_ctl_proc_show(struct seq_file *m, void *v)
-{
-       struct capi_ctr *ctrl = m->private;
-       diva_card *card = (diva_card *) ctrl->driverdata;
-
-       seq_printf(m, "%s\n", ctrl->name);
-       seq_printf(m, "Serial No. : %s\n", ctrl->serial);
-       seq_printf(m, "Id         : %d\n", card->Id);
-       seq_printf(m, "Channels   : %d\n", card->d.channels);
-
-       return 0;
-}
-
-/*
- * set additional os settings in capi_ctr struct
- */
-void diva_os_set_controller_struct(struct capi_ctr *ctrl)
-{
-       ctrl->driver_name = DRIVERLNAME;
-       ctrl->load_firmware = NULL;
-       ctrl->reset_ctr = NULL;
-       ctrl->proc_show = diva_ctl_proc_show;
-       ctrl->owner = THIS_MODULE;
-}
-
-/*
- * module init
- */
-static int __init divacapi_init(void)
-{
-       char tmprev[32];
-       int ret = 0;
-
-       sprintf(DRIVERRELEASE_CAPI, "%d.%d%s", DRRELMAJOR, DRRELMINOR,
-               DRRELEXTRA);
-
-       printk(KERN_INFO "%s\n", DRIVERNAME);
-       printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_CAPI);
-       strcpy(tmprev, main_revision);
-       printk("%s  Build: %s(%s)\n", getrev(tmprev),
-              diva_capi_common_code_build, DIVA_BUILD);
-
-       if (!(init_capifunc())) {
-               printk(KERN_ERR "%s: failed init capi_driver.\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-       }
-
-       return ret;
-}
-
-/*
- * module exit
- */
-static void __exit divacapi_exit(void)
-{
-       finit_capifunc();
-       printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
-}
-
-module_init(divacapi_init);
-module_exit(divacapi_exit);
diff --git a/drivers/isdn/hardware/eicon/cardtype.h b/drivers/isdn/hardware/eicon/cardtype.h
deleted file mode 100644 (file)
index 8b20e22..0000000
+++ /dev/null
@@ -1,1098 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef _CARDTYPE_H_
-#define _CARDTYPE_H_
-#ifndef CARDTYPE_H_WANT_DATA
-#define CARDTYPE_H_WANT_DATA   0
-#endif
-#ifndef CARDTYPE_H_WANT_IDI_DATA
-#define CARDTYPE_H_WANT_IDI_DATA  0
-#endif
-#ifndef CARDTYPE_H_WANT_RESOURCE_DATA
-#define CARDTYPE_H_WANT_RESOURCE_DATA 1
-#endif
-#ifndef CARDTYPE_H_WANT_FILE_DATA
-#define CARDTYPE_H_WANT_FILE_DATA  1
-#endif
-/*
- * D-channel protocol identifiers
- *
- * Attention: Unfortunately the identifiers defined here differ from
- *      the identifiers used in Protocol/1/Common/prot/q931.h .
- *     The only reason for this is that q931.h has not a global
- *     scope and we did not know about the definitions there.
- *     But the definitions here cannot be changed easily because
- *     they are used in setup scripts and programs.
- *     Thus the definitions here have to be mapped if they are
- *     used in the protocol code context !
- *
- * Now the identifiers are defined in the q931lib/constant.h file.
- * Unfortunately this file has also not a global scope.
- * But beginning with PROTTYPE_US any new identifier will get the same
- * value as the corresponding PROT_* definition in q931lib/constant.h !
- */
-#define PROTTYPE_MINVAL     0
-#define PROTTYPE_ETSI       0
-#define PROTTYPE_1TR6       1
-#define PROTTYPE_BELG       2
-#define PROTTYPE_FRANC      3
-#define PROTTYPE_ATEL       4
-#define PROTTYPE_NI         5  /* DMS 100, Nortel, National ISDN */
-#define PROTTYPE_5ESS       6  /* 5ESS   , AT&T,   5ESS Custom   */
-#define PROTTYPE_JAPAN      7
-#define PROTTYPE_SWED       8
-#define PROTTYPE_US         9  /* US autodetect */
-#define PROTTYPE_ITALY      10
-#define PROTTYPE_TWAN       11
-#define PROTTYPE_AUSTRAL    12
-#define PROTTYPE_4ESDN      13
-#define PROTTYPE_4ESDS      14
-#define PROTTYPE_4ELDS      15
-#define PROTTYPE_4EMGC      16
-#define PROTTYPE_4EMGI      17
-#define PROTTYPE_HONGKONG   18
-#define PROTTYPE_RBSCAS     19
-#define PROTTYPE_CORNETN    20
-#define PROTTYPE_QSIG       21
-#define PROTTYPE_NI_EWSD    22 /* EWSD, Siemens, National ISDN   */
-#define PROTTYPE_5ESS_NI    23 /* 5ESS, Lucent, National ISDN    */
-#define PROTTYPE_T1CORNETN  24
-#define PROTTYPE_CORNETNQ   25
-#define PROTTYPE_T1CORNETNQ 26
-#define PROTTYPE_T1QSIG     27
-#define PROTTYPE_E1UNCH     28
-#define PROTTYPE_T1UNCH     29
-#define PROTTYPE_E1CHAN     30
-#define PROTTYPE_T1CHAN     31
-#define PROTTYPE_R2CAS      32
-#define PROTTYPE_MAXVAL     32
-/*
- * Card type identifiers
- */
-#define CARD_UNKNOWN                      0
-#define CARD_NONE                         0
-/* DIVA cards */
-#define CARDTYPE_DIVA_MCA                 0
-#define CARDTYPE_DIVA_ISA                 1
-#define CARDTYPE_DIVA_PCM                 2
-#define CARDTYPE_DIVAPRO_ISA              3
-#define CARDTYPE_DIVAPRO_PCM              4
-#define CARDTYPE_DIVAPICO_ISA             5
-#define CARDTYPE_DIVAPICO_PCM             6
-/* DIVA 2.0 cards */
-#define CARDTYPE_DIVAPRO20_PCI            7
-#define CARDTYPE_DIVA20_PCI               8
-/* S cards */
-#define CARDTYPE_QUADRO_ISA               9
-#define CARDTYPE_S_ISA                    10
-#define CARDTYPE_S_MCA                    11
-#define CARDTYPE_SX_ISA                   12
-#define CARDTYPE_SX_MCA                   13
-#define CARDTYPE_SXN_ISA                  14
-#define CARDTYPE_SXN_MCA                  15
-#define CARDTYPE_SCOM_ISA                 16
-#define CARDTYPE_SCOM_MCA                 17
-#define CARDTYPE_PR_ISA                   18
-#define CARDTYPE_PR_MCA                   19
-/* Diva Server cards (formerly called Maestra, later Amadeo) */
-#define CARDTYPE_MAESTRA_ISA              20
-#define CARDTYPE_MAESTRA_PCI              21
-/* Diva Server cards to be developed (Quadro, Primary rate) */
-#define CARDTYPE_DIVASRV_Q_8M_PCI         22
-#define CARDTYPE_DIVASRV_P_30M_PCI        23
-#define CARDTYPE_DIVASRV_P_2M_PCI         24
-#define CARDTYPE_DIVASRV_P_9M_PCI         25
-/* DIVA 2.0 cards */
-#define CARDTYPE_DIVA20_ISA               26
-#define CARDTYPE_DIVA20U_ISA              27
-#define CARDTYPE_DIVA20U_PCI              28
-#define CARDTYPE_DIVAPRO20_ISA            29
-#define CARDTYPE_DIVAPRO20U_ISA           30
-#define CARDTYPE_DIVAPRO20U_PCI           31
-/* DIVA combi cards (piccola ISDN + rockwell V.34 modem) */
-#define CARDTYPE_DIVAMOBILE_PCM           32
-#define CARDTYPE_TDKGLOBALPRO_PCM         33
-/* DIVA Pro PC OEM card for 'New Media Corporation' */
-#define CARDTYPE_NMC_DIVAPRO_PCM          34
-/* DIVA Pro 2.0 OEM cards for 'British Telecom' */
-#define CARDTYPE_BT_EXLANE_PCI            35
-#define CARDTYPE_BT_EXLANE_ISA            36
-/* DIVA low cost cards, 1st name DIVA 3.0, 2nd DIVA 2.01, 3rd ??? */
-#define CARDTYPE_DIVALOW_ISA              37
-#define CARDTYPE_DIVALOWU_ISA             38
-#define CARDTYPE_DIVALOW_PCI              39
-#define CARDTYPE_DIVALOWU_PCI             40
-/* DIVA combi cards (piccola ISDN + rockwell V.90 modem) */
-#define CARDTYPE_DIVAMOBILE_V90_PCM       41
-#define CARDTYPE_TDKGLOBPRO_V90_PCM       42
-#define CARDTYPE_DIVASRV_P_23M_PCI        43
-#define CARDTYPE_DIVALOW_USB              44
-/* DIVA Audio (CT) family */
-#define CARDTYPE_DIVA_CT_ST               45
-#define CARDTYPE_DIVA_CT_U                46
-#define CARDTYPE_DIVA_CTLITE_ST           47
-#define CARDTYPE_DIVA_CTLITE_U            48
-/* DIVA ISDN plus V.90 series */
-#define CARDTYPE_DIVAISDN_V90_PCM         49
-#define CARDTYPE_DIVAISDN_V90_PCI         50
-#define CARDTYPE_DIVAISDN_TA              51
-/* DIVA Server Voice cards */
-#define CARDTYPE_DIVASRV_VOICE_Q_8M_PCI   52
-/* DIVA Server V2 cards */
-#define CARDTYPE_DIVASRV_Q_8M_V2_PCI      53
-#define CARDTYPE_DIVASRV_P_30M_V2_PCI     54
-/* DIVA Server Voice V2 cards */
-#define CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI 55
-#define CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI 56
-/* Diva LAN */
-#define CARDTYPE_DIVAISDN_LAN             57
-#define CARDTYPE_DIVA_202_PCI_ST          58
-#define CARDTYPE_DIVA_202_PCI_U           59
-#define CARDTYPE_DIVASRV_B_2M_V2_PCI      60
-#define CARDTYPE_DIVASRV_B_2F_PCI         61
-#define CARDTYPE_DIVALOW_USBV2            62
-#define CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI 63
-#define CARDTYPE_DIVA_PRO_30_PCI_ST       64
-#define CARDTYPE_DIVA_CT_ST_V20           65
-/* Diva Mobile V.90 PC Card and Diva ISDN PC Card */
-#define CARDTYPE_DIVAMOBILE_V2_PCM        66
-#define CARDTYPE_DIVA_V2_PCM              67
-/* Re-badged Diva Pro PC Card */
-#define CARDTYPE_DIVA_PC_CARD             68
-/* next free card type identifier */
-#define CARDTYPE_MAX                      69
-/*
- * The card families
- */
-#define FAMILY_DIVA   1
-#define FAMILY_S   2
-#define FAMILY_MAESTRA  3
-#define FAMILY_MAX   4
-/*
- * The basic card types
- */
-#define CARD_DIVA           1  /* DSP based, old DSP */
-#define CARD_PRO            2  /* DSP based, new DSP */
-#define CARD_PICO           3  /* HSCX based   */
-#define CARD_S    4  /* IDI on board based */
-#define CARD_SX    5  /* IDI on board based */
-#define CARD_SXN   6  /* IDI on board based */
-#define CARD_SCOM   7  /* IDI on board based */
-#define CARD_QUAD   8  /* IDI on board based */
-#define CARD_PR    9  /* IDI on board based */
-#define CARD_MAE         10  /* IDI on board based */
-#define CARD_MAEQ        11  /* IDI on board based */
-#define CARD_MAEP        12  /* IDI on board based */
-#define CARD_DIVALOW  13  /* IPAC based   */
-#define CARD_CT    14  /* SCOUT based          */
-#define CARD_DIVATA   15  /* DIVA TA */
-#define CARD_DIVALAN  16  /* DIVA LAN */
-#define CARD_MAE2         17  /* IDI on board based */
-#define CARD_MAX   18
-/*
- * The internal card types of the S family
- */
-#define CARD_I_NONE   0
-#define CARD_I_S   0
-#define CARD_I_SX   1
-#define CARD_I_SCOM   2
-#define CARD_I_QUAD   3
-#define CARD_I_PR   4
-/*
- * The bus types we support
- */
-#define BUS_ISA             1
-#define BUS_PCM             2
-#define BUS_PCI             3
-#define BUS_MCA             4
-#define BUS_USB             5
-#define BUS_COM    6
-#define BUS_LAN    7
-/*
- * The chips we use for B-channel traffic
- */
-#define CHIP_NONE           0
-#define CHIP_DSP            1
-#define CHIP_HSCX           2
-#define CHIP_IPAC           3
-#define CHIP_SCOUT          4
-#define CHIP_EXTERN         5
-#define CHIP_IPACX          6
-/*
- * The structures where the card properties are aggregated by id
- */
-typedef struct CARD_PROPERTIES
-{   char     *Name;  /* official marketing name     */
-       unsigned short PnPId;  /* plug and play ID (for non PCMIA cards) */
-       unsigned short Version; /* major and minor version no of the card */
-       unsigned char DescType; /* card type to set in the IDI descriptor */
-       unsigned char  Family;  /* basic family of the card     */
-       unsigned short  Features; /* features bits to set in the IDI desc. */
-       unsigned char Card;  /* basic card type       */
-       unsigned char IType;  /* internal type of S cards (read from ram) */
-       unsigned char  Bus;  /* bus type this card is designed for  */
-       unsigned char  Chip;  /* chipset used on card      */
-       unsigned char Adapters; /* number of adapters on card    */
-       unsigned char Channels; /* # of channels per adapter    */
-       unsigned short E_info;  /* # of ram entity info structs per adapter */
-       unsigned short SizeIo;  /* size of IO window per adapter   */
-       unsigned short SizeMem; /* size of memory window per adapter  */
-} CARD_PROPERTIES;
-typedef struct CARD_RESOURCE
-{ unsigned char Int[10];
-       unsigned short IoFirst;
-       unsigned short IoStep;
-       unsigned short IoCnt;
-       unsigned long MemFirst;
-       unsigned long MemStep;
-       unsigned short MemCnt;
-} CARD_RESOURCE;
-/* test if the card of type 't' is a plug & play card */
-#define IS_PNP(t)                                              \
-       (                                                       \
-               (                                               \
-                       CardProperties[t].Bus != BUS_ISA        \
-                       &&                                      \
-                       CardProperties[t].Bus != BUS_MCA        \
-                       )                                       \
-               ||                                              \
-               (                                               \
-                       CardProperties[t].Family != FAMILY_S    \
-                       &&                                      \
-                       CardProperties[t].Card != CARD_DIVA     \
-                       )                                       \
-               )
-/* extract IDI Descriptor info for card type 't' (p == DescType/Features) */
-#define IDI_PROP(t, p) (CardProperties[t].p)
-#if CARDTYPE_H_WANT_DATA
-#if CARDTYPE_H_WANT_IDI_DATA
-/* include "di_defs.h" for IDI adapter type and feature flag definitions */
-#include "di_defs.h"
-#else /*!CARDTYPE_H_WANT_IDI_DATA*/
-/* define IDI adapter types and feature flags here to prevent inclusion  */
-#ifndef IDI_ADAPTER_S
-#define IDI_ADAPTER_S           1
-#define IDI_ADAPTER_PR          2
-#define IDI_ADAPTER_DIVA        3
-#define IDI_ADAPTER_MAESTRA     4
-#endif
-#ifndef DI_VOICE
-#define DI_VOICE          0x0 /* obsolete define */
-#define DI_FAX3           0x1
-#define DI_MODEM          0x2
-#define DI_POST           0x4
-#define DI_V110           0x8
-#define DI_V120           0x10
-#define DI_POTS           0x20
-#define DI_CODEC          0x40
-#define DI_MANAGE         0x80
-#define DI_V_42           0x0100
-#define DI_EXTD_FAX       0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
-#define DI_AT_PARSER      0x0400 /* Build-in AT Parser in the L2 */
-#define DI_VOICE_OVER_IP  0x0800 /* Voice over IP support */
-#endif
-#endif /*CARDTYPE_H_WANT_IDI_DATA*/
-#define DI_V1x0         (DI_V110 | DI_V120)
-#define DI_NULL         0x0000
-#if defined(SOFT_DSP_SUPPORT)
-#define SOFT_DSP_ADD_FEATURES  (DI_MODEM | DI_FAX3 | DI_AT_PARSER)
-#else
-#define SOFT_DSP_ADD_FEATURES  0
-#endif
-#if defined(SOFT_V110_SUPPORT)
-#define DI_SOFT_V110  DI_V110
-#else
-#define DI_SOFT_V110  0
-#endif
-/*--- CardProperties [Index=CARDTYPE_....] ---------------------------------*/
-CARD_PROPERTIES CardProperties[] =
-{
-       { /*  0  */
-               "Diva MCA",       0x6336,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
-               CARD_DIVA,   CARD_I_NONE, BUS_MCA, CHIP_DSP,
-               1, 2,  0,   8,      0
-       },
-       { /*  1  */
-               "Diva ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
-               CARD_DIVA,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,  0,   8,      0
-       },
-       { /*  2  */
-               "Diva/PCM",       0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
-               CARD_DIVA,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
-               1, 2,  0,   8,      0
-       },
-       { /*  3  */
-               "Diva PRO ISA",      0x0031,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,  0,   8,      0
-       },
-       { /*  4  */
-               "Diva PRO PC-Card",     0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
-               1, 2,   0,   8,      0
-       },
-       { /*  5  */
-               "Diva PICCOLA ISA",     0x0051,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
-               1, 2,   0,   8,      0
-       },
-       { /*  6  */
-               "Diva PICCOLA PCM",     0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
-               1, 2,   0,   8,      0
-       },
-       { /*  7  */
-               "Diva PRO 2.0 S/T PCI",    0xe001,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   0,   8,      0
-       },
-       { /*  8  */
-               "Diva 2.0 S/T PCI",     0xe002,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCI, CHIP_HSCX,
-               1, 2,   0,   8,      0
-       },
-       { /*  9  */
-               "QUADRO ISA",      0x0000,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
-               CARD_QUAD,   CARD_I_QUAD, BUS_ISA, CHIP_NONE,
-               4, 2,   16,  0,  0x800
-       },
-       { /* 10  */
-               "S ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
-               CARD_S,    CARD_I_S,  BUS_ISA, CHIP_NONE,
-               1, 1,   16,  0,  0x800
-       },
-       { /* 11  */
-               "S MCA",       0x6a93,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
-               CARD_S,    CARD_I_S,  BUS_MCA, CHIP_NONE,
-               1, 1,   16,  16,  0x400
-       },
-       { /* 12 */
-               "SX ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
-               CARD_SX,   CARD_I_SX,  BUS_ISA, CHIP_NONE,
-               1, 2,  16,  0,  0x800
-       },
-       { /* 13 */
-               "SX MCA",       0x6a93,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
-               CARD_SX,   CARD_I_SX,  BUS_MCA, CHIP_NONE,
-               1, 2,  16,  16,  0x400
-       },
-       { /* 14 */
-               "SXN ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
-               CARD_SXN,   CARD_I_SCOM, BUS_ISA, CHIP_NONE,
-               1, 2,   16,  0,   0x800
-       },
-       { /* 15 */
-               "SXN MCA",       0x6a93,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
-               CARD_SXN,   CARD_I_SCOM, BUS_MCA, CHIP_NONE,
-               1, 2,  16,  16,  0x400
-       },
-       { /* 16 */
-               "SCOM ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
-               CARD_SCOM,   CARD_I_SCOM, BUS_ISA, CHIP_NONE,
-               1, 2,   16,  0,   0x800
-       },
-       { /* 17 */
-               "SCOM MCA",       0x6a93,  0x0100,
-               IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
-               CARD_SCOM,   CARD_I_SCOM, BUS_MCA, CHIP_NONE,
-               1, 2,  16,  16,  0x400
-       },
-       { /* 18 */
-               "S2M ISA",       0x0000,  0x0100,
-               IDI_ADAPTER_PR,  FAMILY_S,  DI_NULL,
-               CARD_PR,   CARD_I_PR,  BUS_ISA, CHIP_NONE,
-               1, 30,  256, 0,   0x4000
-       },
-       { /* 19 */
-               "S2M MCA",       0x6abb,  0x0100,
-               IDI_ADAPTER_PR,  FAMILY_S,  DI_NULL,
-               CARD_PR,   CARD_I_PR,  BUS_MCA, CHIP_NONE,
-               1, 30,  256, 16,  0x4000
-       },
-       { /* 20 */
-               "Diva Server BRI-2M ISA",   0x0041,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAE,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,   16,  8,  0
-       },
-       { /* 21 */
-               "Diva Server BRI-2M PCI",   0xE010,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAE,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   16,  8,   0
-       },
-       { /* 22 */
-               "Diva Server 4BRI-8M PCI",   0xE012,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               4, 2,   16,  8,   0
-       },
-       { /* 23 */
-               "Diva Server PRI-30M PCI",   0xE014,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,  256,  8,   0
-       },
-       { /* 24 */
-               "Diva Server PRI-2M PCI",   0xe014,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,  256,  8,   0
-       },
-       { /* 25 */
-               "Diva Server PRI-9M PCI",   0x0000,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,     256,  8,   0
-       },
-       { /* 26 */
-               "Diva 2.0 S/T ISA",     0x0071,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
-               1, 2,  0,   8,   0
-       },
-       { /* 27 */
-               "Diva 2.0 U ISA",     0x0091,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
-               1, 2,   0,   8,   0
-       },
-       { /* 28 */
-               "Diva 2.0 U PCI",     0xe004,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCI, CHIP_HSCX,
-               1, 2,   0,   8,   0
-       },
-       { /* 29 */
-               "Diva PRO 2.0 S/T ISA",    0x0061,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,  0,   8,   0
-       },
-       { /* 30 */
-               "Diva PRO 2.0 U ISA",    0x0081,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,  0,   8,   0
-       },
-       { /* 31 */
-               "Diva PRO 2.0 U PCI",    0xe003,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   0,   8,   0
-       },
-       { /* 32 */
-               "Diva MOBILE",      0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
-               1, 2,  0,   8,   0
-       },
-       { /* 33 */
-               "TDK DFI3600",      0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
-               1, 2,  0,   8,   0
-       },
-       { /* 34 (OEM version of 4 - "Diva PRO PC-Card") */
-               "New Media ISDN",     0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
-               1, 2,   0,   8,   0
-       },
-       { /* 35 (OEM version of 7 - "Diva PRO 2.0 S/T PCI") */
-               "BT ExLane PCI",     0xe101,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   0,   8,   0
-       },
-       { /* 36 (OEM version of 29 - "Diva PRO 2.0 S/T ISA") */
-               "BT ExLane ISA",     0x1061,  0x0200,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
-               CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
-               1, 2,   0,   8,   0
-       },
-       { /* 37 */
-               "Diva 2.01 S/T ISA",    0x00A1,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_ISA, CHIP_IPAC,
-               1, 2,   0,   8,      0
-       },
-       { /* 38 */
-               "Diva 2.01 U ISA",     0x00B1,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_ISA, CHIP_IPAC,
-               1, 2,   0,   8,      0
-       },
-       { /* 39 */
-               "Diva 2.01 S/T PCI",    0xe005,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
-               1, 2,   0,   8,   0
-       },
-       { /* 40        no ID yet */
-               "Diva 2.01 U PCI",     0x0000,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
-               1, 2,   0,   8,   0
-       },
-       { /* 41 */
-               "Diva MOBILE V.90",     0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
-               1, 2,  0,   8,   0
-       },
-       { /* 42 */
-               "TDK DFI3600 V.90",     0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
-               1, 2,  0,   8,   0
-       },
-       { /* 43 */
-               "Diva Server PRI-23M PCI",   0xe014,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,  256,  8,   0
-       },
-       { /* 44 */
-               "Diva 2.01 S/T USB",    0x1000,     0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_USB, CHIP_IPAC,
-               1,  2,  0,  8,   0
-       },
-       { /* 45 */
-               "Diva CT S/T PCI",    0xe006,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 46 */
-               "Diva CT U PCI",     0xe007,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 47 */
-               "Diva CT Lite S/T PCI",   0xe008,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 48 */
-               "Diva CT Lite U PCI",   0xe009,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 49 */
-               "Diva ISDN+V.90 PC Card", 0x8D8C, 0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_DIVALOW, CARD_I_NONE, BUS_PCM, CHIP_IPAC,
-               1, 2,  0,   8,   0
-       },
-       { /* 50 */
-               "Diva ISDN+V.90 PCI",    0xe00A,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120  | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
-               1, 2,   0,   8,   0
-       },
-       { /* 51 (DivaTA)      no ID */
-               "Diva TA",       0x0000,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVATA,  CARD_I_NONE, BUS_COM, CHIP_EXTERN,
-               1, 1,   0,   8,   0
-       },
-       { /* 52 (Diva Server 4BRI-8M PCI adapter enabled for Voice) */
-               "Diva Server Voice 4BRI-8M PCI", 0xE016,  0x0100,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
-               CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               4, 2,   16,  8,   0
-       },
-       { /* 53 (Diva Server 4BRI 2.0 adapter) */
-               "Diva Server 4BRI-8M 2.0 PCI",  0xE013,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               4, 2,   16,  8,   0
-       },
-       { /* 54 (Diva Server PRI 2.0 adapter) */
-               "Diva Server PRI 2.0 PCI",   0xE015,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,  256,  8,   0
-       },
-       { /* 55 (Diva Server 4BRI-8M 2.0 PCI adapter enabled for Voice) */
-               "Diva Server Voice 4BRI-8M 2.0 PCI", 0xE017,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
-               CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               4, 2,   16,  8,   0
-       },
-       { /* 56 (Diva Server PRI 2.0 PCI adapter enabled for Voice) */
-               "Diva Server Voice PRI 2.0 PCI",  0xE019,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
-               CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 30,  256,  8,   0
-       },
-       {
-               /* 57 (DivaLan )      no ID */
-               "Diva LAN",       0x0000,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALAN,  CARD_I_NONE, BUS_LAN, CHIP_EXTERN,
-               1, 1,   0,   8,   0
-       },
-       { /* 58 */
-               "Diva 2.02 PCI S/T",    0xE00B,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES | DI_SOFT_V110,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPACX,
-               1, 2,   0,   8,   0
-       },
-       { /* 59 */
-               "Diva 2.02 PCI U",     0xE00C,  0x0300,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPACX,
-               1, 2,   0,   8,   0
-       },
-       { /* 60 */
-               "Diva Server BRI-2M 2.0 PCI",     0xE018,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_MAE2,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   16,  8,   0
-       },
-       { /* 61  (the previous name was Diva Server BRI-2F 2.0 PCI) */
-               "Diva Server 2FX",                      0xE01A,     0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_SOFT_V110,
-               CARD_MAE2,          CARD_I_NONE,    BUS_PCI,    CHIP_IPACX,
-               1,  2,      16,     8,   0
-       },
-       { /* 62 */
-               " Diva ISDN USB 2.0",    0x1003,     0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_DIVALOW,  CARD_I_NONE, BUS_USB, CHIP_IPACX,
-               1, 2,  0,  8,   0
-       },
-       { /* 63 (Diva Server BRI-2M 2.0 PCI adapter enabled for Voice) */
-               "Diva Server Voice BRI-2M 2.0 PCI", 0xE01B,  0x0200,
-               IDI_ADAPTER_MAESTRA, FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
-               CARD_MAE2,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1, 2,   16,  8,   0
-       },
-       { /* 64 */
-               "Diva Pro 3.0 PCI",    0xe00d,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
-               CARD_PRO,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 65 */
-               "Diva ISDN + CT 2.0",    0xE00E,  0x0300,
-               IDI_ADAPTER_DIVA   , FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
-               CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
-               1,  2,  0,  0,   0
-       },
-       { /* 66 */
-               "Diva Mobile V.90 PC Card",  0x8331,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_IPACX,
-               1, 2,  0,   8,   0
-       },
-       { /* 67 */
-               "Diva ISDN PC Card",  0x8311,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_IPACX,
-               1, 2,  0,   8,   0
-       },
-       { /* 68 */
-               "Diva ISDN PC Card",  0x0000,  0x0100,
-               IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
-               CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
-               1, 2,   0,   8,      0
-       },
-};
-#if CARDTYPE_H_WANT_RESOURCE_DATA
-/*--- CardResource [Index=CARDTYPE_....]   ---------------------------(GEI)-*/
-CARD_RESOURCE CardResource[] = {
-/*   Interrupts     IO-Address   Mem-Address */
-       /* 0*/ {  3,4,9,0,0,0,0,0,0,0,   0x200,0x20,16,   0x0,0x0,0   }, // DIVA MCA
-       /* 1*/ {  3,4,9,10,11,12,0,0,0,0,  0x200,0x20,16,   0x0,0x0,0   }, // DIVA ISA
-       /* 2*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PCMCIA
-       /* 3*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO ISA
-       /* 4*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO PCMCIA
-       /* 5*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA PICCOLA ISA
-       /* 6*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA PICCOLA PCMCIA
-       /* 7*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO 2.0 PCI
-       /* 8*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.0 PCI
-       /* 9*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x2000,64 }, // QUADRO ISA
-       /*10*/ {  3,4,9,10,11,12,0,0,0,0,  0x0,0x0,0,   0xc0000,0x2000,16 }, // S ISA
-       /*11*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // S MCA
-       /*12*/ {  3,4,9,10,11,12,0,0,0,0,  0x0,0x0,0,   0xc0000,0x2000,16 }, // SX ISA
-       /*13*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SX MCA
-       /*14*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x0800,256 }, // SXN ISA
-       /*15*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SXN MCA
-       /*16*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x0800,256 }, // SCOM ISA
-       /*17*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SCOM MCA
-       /*18*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0xc0000,0x4000,16 }, // S2M ISA
-       /*19*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x4000,16 }, // S2M MCA
-       /*20*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA ISA
-       /*21*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA PCI
-       /*22*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA QUADRO ISA
-       /*23*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA QUADRO PCI
-       /*24*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA PRIMARY ISA
-       /*25*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA PRIMARY PCI
-       /*26*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.0 ISA
-       /*27*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.0 /U ISA
-       /*28*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.0 /U PCI
-       /*29*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO 2.0 ISA
-       /*30*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO 2.0 /U ISA
-       /*31*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO 2.0 /U PCI
-       /*32*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA MOBILE
-       /*33*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // TDK DFI3600 (same as DIVA MOBILE [32])
-       /*34*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // New Media ISDN (same as DIVA PRO PCMCIA [4])
-       /*35*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // BT ExLane PCI (same as DIVA PRO 2.0 PCI [7])
-       /*36*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // BT ExLane ISA (same as DIVA PRO 2.0 ISA [29])
-       /*37*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.01 S/T ISA
-       /*38*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.01 U ISA
-       /*39*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.01 S/T PCI
-       /*40*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.01 U PCI
-       /*41*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA MOBILE V.90
-       /*42*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // TDK DFI3600 V.90 (same as DIVA MOBILE V.90 [39])
-       /*43*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // DIVA Server PRI-23M PCI
-       /*44*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA 2.01 S/T USB
-       /*45*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT S/T PCI
-       /*46*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT U PCI
-       /*47*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT Lite S/T PCI
-       /*48*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT Lite U PCI
-       /*49*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA ISDN+V.90 PC Card
-       /*50*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA ISDN+V.90 PCI
-       /*51*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA TA
-       /*52*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
-       /*53*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
-       /*54*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA VOICE PRIMARY PCI
-       /*55*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
-       /*56*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA VOICE PRIMARY PCI
-       /*57*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA LAN
-       /*58*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.02 S/T PCI
-       /*59*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.02 U PCI
-       /*60*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server BRI-2M 2.0 PCI
-       /*61*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server BRI-2F PCI
-       /*62*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA 2.01 S/T USB
-       /*63*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server Voice BRI-2M 2.0 PCI
-       /*64*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 3.0 PCI
-       /*65*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT S/T PCI V2.0
-       /*66*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA Mobile V.90 PC Card
-       /*67*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA ISDN PC Card
-       /*68*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA ISDN PC Card
-};
-#endif /*CARDTYPE_H_WANT_RESOURCE_DATA*/
-#else /*!CARDTYPE_H_WANT_DATA*/
-extern CARD_PROPERTIES  CardProperties[];
-extern CARD_RESOURCE  CardResource[];
-#endif /*CARDTYPE_H_WANT_DATA*/
-/*
- * all existing download files
- */
-#define CARD_DSP_CNT  5
-#define CARD_PROT_CNT  9
-#define CARD_FT_UNKNOWN     0
-#define CARD_FT_B   1
-#define CARD_FT_D   2
-#define CARD_FT_S   3
-#define CARD_FT_M   4
-#define CARD_FT_NEW_DSP_COMBIFILE 5  /* File format of new DSP code (the DSP code powered by Telindus) */
-#define CARD_FILE_NONE      0
-#define CARD_B_S   1
-#define CARD_B_P   2
-#define CARD_D_K1   3
-#define CARD_D_K2   4
-#define CARD_D_H   5
-#define CARD_D_V   6
-#define CARD_D_M   7
-#define CARD_D_F   8
-#define CARD_P_S_E   9
-#define CARD_P_S_1   10
-#define CARD_P_S_B   11
-#define CARD_P_S_F   12
-#define CARD_P_S_A   13
-#define CARD_P_S_N   14
-#define CARD_P_S_5   15
-#define CARD_P_S_J   16
-#define CARD_P_SX_E   17
-#define CARD_P_SX_1   18
-#define CARD_P_SX_B   19
-#define CARD_P_SX_F   20
-#define CARD_P_SX_A   21
-#define CARD_P_SX_N   22
-#define CARD_P_SX_5   23
-#define CARD_P_SX_J   24
-#define CARD_P_SY_E   25
-#define CARD_P_SY_1   26
-#define CARD_P_SY_B   27
-#define CARD_P_SY_F   28
-#define CARD_P_SY_A   29
-#define CARD_P_SY_N   30
-#define CARD_P_SY_5   31
-#define CARD_P_SY_J   32
-#define CARD_P_SQ_E   33
-#define CARD_P_SQ_1   34
-#define CARD_P_SQ_B   35
-#define CARD_P_SQ_F   36
-#define CARD_P_SQ_A   37
-#define CARD_P_SQ_N   38
-#define CARD_P_SQ_5   39
-#define CARD_P_SQ_J   40
-#define CARD_P_P_E   41
-#define CARD_P_P_1   42
-#define CARD_P_P_B   43
-#define CARD_P_P_F   44
-#define CARD_P_P_A   45
-#define CARD_P_P_N   46
-#define CARD_P_P_5   47
-#define CARD_P_P_J   48
-#define CARD_P_M_E   49
-#define CARD_P_M_1   50
-#define CARD_P_M_B   51
-#define CARD_P_M_F   52
-#define CARD_P_M_A   53
-#define CARD_P_M_N   54
-#define CARD_P_M_5   55
-#define CARD_P_M_J   56
-#define CARD_P_S_S   57
-#define CARD_P_SX_S   58
-#define CARD_P_SY_S   59
-#define CARD_P_SQ_S   60
-#define CARD_P_P_S   61
-#define CARD_P_M_S   62
-#define CARD_D_NEW_DSP_COMBIFILE 63
-typedef struct CARD_FILES_DATA
-{
-       char *Name;
-       unsigned char  Type;
-}
-       CARD_FILES_DATA;
-typedef struct CARD_FILES
-{
-       unsigned char  Boot;
-       unsigned char  Dsp[CARD_DSP_CNT];
-       unsigned char  DspTelindus;
-       unsigned char  Prot[CARD_PROT_CNT];
-}
-       CARD_FILES;
-#if CARDTYPE_H_WANT_DATA
-#if CARDTYPE_H_WANT_FILE_DATA
-CARD_FILES_DATA CardFData[] = {
-// Filename   Filetype
-       0,     CARD_FT_UNKNOWN,
-       "didnload.bin",  CARD_FT_B,
-       "diprload.bin",  CARD_FT_B,
-       "didiva.bin",  CARD_FT_D,
-       "didivapp.bin",  CARD_FT_D,
-       "dihscx.bin",  CARD_FT_D,
-       "div110.bin",  CARD_FT_D,
-       "dimodem.bin",  CARD_FT_D,
-       "difax.bin",  CARD_FT_D,
-       "di_etsi.bin",  CARD_FT_S,
-       "di_1tr6.bin",  CARD_FT_S,
-       "di_belg.bin",  CARD_FT_S,
-       "di_franc.bin",  CARD_FT_S,
-       "di_atel.bin",  CARD_FT_S,
-       "di_ni.bin",  CARD_FT_S,
-       "di_5ess.bin",  CARD_FT_S,
-       "di_japan.bin",  CARD_FT_S,
-       "di_etsi.sx",  CARD_FT_S,
-       "di_1tr6.sx",  CARD_FT_S,
-       "di_belg.sx",  CARD_FT_S,
-       "di_franc.sx",  CARD_FT_S,
-       "di_atel.sx",  CARD_FT_S,
-       "di_ni.sx",   CARD_FT_S,
-       "di_5ess.sx",  CARD_FT_S,
-       "di_japan.sx",  CARD_FT_S,
-       "di_etsi.sy",  CARD_FT_S,
-       "di_1tr6.sy",  CARD_FT_S,
-       "di_belg.sy",  CARD_FT_S,
-       "di_franc.sy",  CARD_FT_S,
-       "di_atel.sy",  CARD_FT_S,
-       "di_ni.sy",   CARD_FT_S,
-       "di_5ess.sy",  CARD_FT_S,
-       "di_japan.sy",  CARD_FT_S,
-       "di_etsi.sq",  CARD_FT_S,
-       "di_1tr6.sq",  CARD_FT_S,
-       "di_belg.sq",  CARD_FT_S,
-       "di_franc.sq",  CARD_FT_S,
-       "di_atel.sq",  CARD_FT_S,
-       "di_ni.sq",   CARD_FT_S,
-       "di_5ess.sq",  CARD_FT_S,
-       "di_japan.sq",  CARD_FT_S,
-       "di_etsi.p",  CARD_FT_S,
-       "di_1tr6.p",  CARD_FT_S,
-       "di_belg.p",  CARD_FT_S,
-       "di_franc.p",  CARD_FT_S,
-       "di_atel.p",  CARD_FT_S,
-       "di_ni.p",   CARD_FT_S,
-       "di_5ess.p",  CARD_FT_S,
-       "di_japan.p",  CARD_FT_S,
-       "di_etsi.sm",  CARD_FT_M,
-       "di_1tr6.sm",  CARD_FT_M,
-       "di_belg.sm",  CARD_FT_M,
-       "di_franc.sm",  CARD_FT_M,
-       "di_atel.sm",  CARD_FT_M,
-       "di_ni.sm",   CARD_FT_M,
-       "di_5ess.sm",  CARD_FT_M,
-       "di_japan.sm",  CARD_FT_M,
-       "di_swed.bin",  CARD_FT_S,
-       "di_swed.sx",  CARD_FT_S,
-       "di_swed.sy",  CARD_FT_S,
-       "di_swed.sq",  CARD_FT_S,
-       "di_swed.p",  CARD_FT_S,
-       "di_swed.sm",  CARD_FT_M,
-       "didspdld.bin",     CARD_FT_NEW_DSP_COMBIFILE
-};
-CARD_FILES CardFiles[] =
-{
-       { /* CARD_UNKNOWN */
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       },
-       { /* CARD_DIVA */
-               CARD_FILE_NONE,
-               CARD_D_K1, CARD_D_H, CARD_D_V, CARD_FILE_NONE, CARD_D_F,
-               CARD_D_NEW_DSP_COMBIFILE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       },
-       { /* CARD_PRO  */
-               CARD_FILE_NONE,
-               CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
-               CARD_D_NEW_DSP_COMBIFILE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       },
-       { /* CARD_PICO */
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       },
-       { /* CARD_S    */
-               CARD_B_S,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_S_E, CARD_P_S_1, CARD_P_S_B, CARD_P_S_F,
-               CARD_P_S_A, CARD_P_S_N, CARD_P_S_5, CARD_P_S_J,
-               CARD_P_S_S
-       },
-       { /* CARD_SX   */
-               CARD_B_S,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_SX_E, CARD_P_SX_1, CARD_P_SX_B, CARD_P_SX_F,
-               CARD_P_SX_A, CARD_P_SX_N, CARD_P_SX_5, CARD_P_SX_J,
-               CARD_P_SX_S
-       },
-       { /* CARD_SXN  */
-               CARD_B_S,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
-               CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
-               CARD_P_SY_S
-       },
-       { /* CARD_SCOM */
-               CARD_B_S,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
-               CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
-               CARD_P_SY_S
-       },
-       { /* CARD_QUAD */
-               CARD_B_S,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_SQ_E, CARD_P_SQ_1, CARD_P_SQ_B, CARD_P_SQ_F,
-               CARD_P_SQ_A, CARD_P_SQ_N, CARD_P_SQ_5, CARD_P_SQ_J,
-               CARD_P_SQ_S
-       },
-       { /* CARD_PR   */
-               CARD_B_P,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_P_P_E, CARD_P_P_1, CARD_P_P_B, CARD_P_P_F,
-               CARD_P_P_A, CARD_P_P_N, CARD_P_P_5, CARD_P_P_J,
-               CARD_P_P_S
-       },
-       { /* CARD_MAE  */
-               CARD_FILE_NONE,
-               CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
-               CARD_D_NEW_DSP_COMBIFILE,
-               CARD_P_M_E, CARD_P_M_1, CARD_P_M_B, CARD_P_M_F,
-               CARD_P_M_A, CARD_P_M_N, CARD_P_M_5, CARD_P_M_J,
-               CARD_P_M_S
-       },
-       { /* CARD_MAEQ */  /* currently not supported */
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       },
-       { /* CARD_MAEP */  /* currently not supported */
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
-               CARD_FILE_NONE
-       }
-};
-#endif /*CARDTYPE_H_WANT_FILE_DATA*/
-#else /*!CARDTYPE_H_WANT_DATA*/
-extern CARD_FILES_DATA  CardFData[];
-extern CARD_FILES   CardFiles[];
-#endif /*CARDTYPE_H_WANT_DATA*/
-#endif /* _CARDTYPE_H_ */
diff --git a/drivers/isdn/hardware/eicon/cp_vers.h b/drivers/isdn/hardware/eicon/cp_vers.h
deleted file mode 100644 (file)
index c97230c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-static char diva_capi_common_code_build[] = "102-28";
diff --git a/drivers/isdn/hardware/eicon/dadapter.c b/drivers/isdn/hardware/eicon/dadapter.c
deleted file mode 100644 (file)
index 5142099..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "pc.h"
-#include "debuglib.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "dadapter.h"
-/* --------------------------------------------------------------------------
-   Adapter array change notification framework
-   -------------------------------------------------------------------------- */
-typedef struct _didd_adapter_change_notification {
-       didd_adapter_change_callback_t callback;
-       void IDI_CALL_ENTITY_T *context;
-} didd_adapter_change_notification_t,                          \
-       * IDI_CALL_ENTITY_T pdidd_adapter_change_notification_t;
-#define DIVA_DIDD_MAX_NOTIFICATIONS 256
-static didd_adapter_change_notification_t      \
-NotificationTable[DIVA_DIDD_MAX_NOTIFICATIONS];
-/* --------------------------------------------------------------------------
-   Array to held adapter information
-   -------------------------------------------------------------------------- */
-static DESCRIPTOR  HandleTable[NEW_MAX_DESCRIPTORS];
-static dword Adapters = 0; /* Number of adapters */
-/* --------------------------------------------------------------------------
-   Shadow IDI_DIMAINT
-   and 'shadow' debug stuff
-   -------------------------------------------------------------------------- */
-static void no_printf(unsigned char *format, ...)
-{
-#ifdef EBUG
-       va_list ap;
-       va_start(ap, format);
-       debug((format, ap));
-       va_end(ap);
-#endif
-}
-
-/* -------------------------------------------------------------------------
-   Portable debug Library
-   ------------------------------------------------------------------------- */
-#include "debuglib.c"
-
-static DESCRIPTOR  MAdapter =  {IDI_DIMAINT, /* Adapter Type */
-                               0x00,     /* Channels */
-                               0x0000,    /* Features */
-                               (IDI_CALL)no_printf};
-/* --------------------------------------------------------------------------
-   DAdapter. Only IDI clients with buffer, that is huge enough to
-   get all descriptors will receive information about DAdapter
-   { byte type, byte channels, word features, IDI_CALL request }
-   -------------------------------------------------------------------------- */
-static void IDI_CALL_LINK_T diva_dadapter_request(ENTITY IDI_CALL_ENTITY_T *);
-static DESCRIPTOR  DAdapter =  {IDI_DADAPTER, /* Adapter Type */
-                               0x00,     /* Channels */
-                               0x0000,    /* Features */
-                               diva_dadapter_request };
-/* --------------------------------------------------------------------------
-   LOCALS
-   -------------------------------------------------------------------------- */
-static dword diva_register_adapter_callback(\
-       didd_adapter_change_callback_t callback,
-       void IDI_CALL_ENTITY_T *context);
-static void diva_remove_adapter_callback(dword handle);
-static void diva_notify_adapter_change(DESCRIPTOR *d, int removal);
-static diva_os_spin_lock_t didd_spin;
-/* --------------------------------------------------------------------------
-   Should be called as first step, after driver init
-   -------------------------------------------------------------------------- */
-void diva_didd_load_time_init(void) {
-       memset(&HandleTable[0], 0x00, sizeof(HandleTable));
-       memset(&NotificationTable[0], 0x00, sizeof(NotificationTable));
-       diva_os_initialize_spin_lock(&didd_spin, "didd");
-}
-/* --------------------------------------------------------------------------
-   Should be called as last step, if driver does unload
-   -------------------------------------------------------------------------- */
-void diva_didd_load_time_finit(void) {
-       diva_os_destroy_spin_lock(&didd_spin, "didd");
-}
-/* --------------------------------------------------------------------------
-   Called in order to register new adapter in adapter array
-   return adapter handle (> 0) on success
-   return -1 adapter array overflow
-   -------------------------------------------------------------------------- */
-static int diva_didd_add_descriptor(DESCRIPTOR *d) {
-       diva_os_spin_lock_magic_t      irql;
-       int i;
-       if (d->type == IDI_DIMAINT) {
-               if (d->request) {
-                       MAdapter.request = d->request;
-                       dprintf = (DIVA_DI_PRINTF)d->request;
-                       diva_notify_adapter_change(&MAdapter, 0); /* Inserted */
-                       DBG_TRC(("DIMAINT registered, dprintf=%08x", d->request))
-                               } else {
-                       DBG_TRC(("DIMAINT removed"))
-                               diva_notify_adapter_change(&MAdapter, 1); /* About to remove */
-                       MAdapter.request = (IDI_CALL)no_printf;
-                       dprintf = no_printf;
-               }
-               return (NEW_MAX_DESCRIPTORS);
-       }
-       for (i = 0; i < NEW_MAX_DESCRIPTORS; i++) {
-               diva_os_enter_spin_lock(&didd_spin, &irql, "didd_add");
-               if (HandleTable[i].type == 0) {
-                       memcpy(&HandleTable[i], d, sizeof(*d));
-                       Adapters++;
-                       diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add");
-                       diva_notify_adapter_change(d, 0); /* we have new adapter */
-                       DBG_TRC(("Add adapter[%d], request=%08x", (i + 1), d->request))
-                               return (i + 1);
-               }
-               diva_os_leave_spin_lock(&didd_spin, &irql, "didd_add");
-       }
-       DBG_ERR(("Can't add adapter, out of resources"))
-               return (-1);
-}
-/* --------------------------------------------------------------------------
-   Called in order to remove one registered adapter from array
-   return adapter handle (> 0) on success
-   return 0 on success
-   -------------------------------------------------------------------------- */
-static int diva_didd_remove_descriptor(IDI_CALL request) {
-       diva_os_spin_lock_magic_t      irql;
-       int i;
-       if (request == MAdapter.request) {
-               DBG_TRC(("DIMAINT removed"))
-                       dprintf = no_printf;
-               diva_notify_adapter_change(&MAdapter, 1); /* About to remove */
-               MAdapter.request = (IDI_CALL)no_printf;
-               return (0);
-       }
-       for (i = 0; (Adapters && (i < NEW_MAX_DESCRIPTORS)); i++) {
-               if (HandleTable[i].request == request) {
-                       diva_notify_adapter_change(&HandleTable[i], 1); /* About to remove */
-                       diva_os_enter_spin_lock(&didd_spin, &irql, "didd_rm");
-                       memset(&HandleTable[i], 0x00, sizeof(HandleTable[0]));
-                       Adapters--;
-                       diva_os_leave_spin_lock(&didd_spin, &irql, "didd_rm");
-                       DBG_TRC(("Remove adapter[%d], request=%08x", (i + 1), request))
-                               return (0);
-               }
-       }
-       DBG_ERR(("Invalid request=%08x, can't remove adapter", request))
-               return (-1);
-}
-/* --------------------------------------------------------------------------
-   Read adapter array
-   return 1 if not enough space to save all available adapters
-   -------------------------------------------------------------------------- */
-static int diva_didd_read_adapter_array(DESCRIPTOR *buffer, int length) {
-       diva_os_spin_lock_magic_t      irql;
-       int src, dst;
-       memset(buffer, 0x00, length);
-       length /= sizeof(DESCRIPTOR);
-       DBG_TRC(("DIDD_Read, space = %d, Adapters = %d", length, Adapters + 2))
-
-               diva_os_enter_spin_lock(&didd_spin, &irql, "didd_read");
-       for (src = 0, dst = 0;
-            (Adapters && (src < NEW_MAX_DESCRIPTORS) && (dst < length));
-            src++) {
-               if (HandleTable[src].type) {
-                       memcpy(&buffer[dst], &HandleTable[src], sizeof(DESCRIPTOR));
-                       dst++;
-               }
-       }
-       diva_os_leave_spin_lock(&didd_spin, &irql, "didd_read");
-       if (dst < length) {
-               memcpy(&buffer[dst], &MAdapter, sizeof(DESCRIPTOR));
-               dst++;
-       } else {
-               DBG_ERR(("Can't write DIMAINT. Array too small"))
-                       }
-       if (dst < length) {
-               memcpy(&buffer[dst], &DAdapter, sizeof(DESCRIPTOR));
-               dst++;
-       } else {
-               DBG_ERR(("Can't write DADAPTER. Array too small"))
-                       }
-       DBG_TRC(("Read %d adapters", dst))
-               return (dst == length);
-}
-/* --------------------------------------------------------------------------
-   DAdapter request function.
-   This function does process only synchronous requests, and is used
-   for reception/registration of new interfaces
-   -------------------------------------------------------------------------- */
-static void IDI_CALL_LINK_T diva_dadapter_request(     \
-       ENTITY IDI_CALL_ENTITY_T *e) {
-       IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e;
-       if (e->Req) { /* We do not process it, also return error */
-               e->Rc = OUT_OF_RESOURCES;
-               DBG_ERR(("Can't process async request, Req=%02x", e->Req))
-                       return;
-       }
-       /*
-         So, we process sync request
-       */
-       switch (e->Rc) {
-       case IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY: {
-               diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info;
-               pinfo->handle = diva_register_adapter_callback(         \
-                       (didd_adapter_change_callback_t)pinfo->callback,
-                       (void IDI_CALL_ENTITY_T *)pinfo->context);
-               e->Rc = 0xff;
-       } break;
-       case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY: {
-               diva_didd_adapter_notify_t *pinfo = &syncReq->didd_notify.info;
-               diva_remove_adapter_callback(pinfo->handle);
-               e->Rc = 0xff;
-       } break;
-       case IDI_SYNC_REQ_DIDD_ADD_ADAPTER: {
-               diva_didd_add_adapter_t *pinfo = &syncReq->didd_add_adapter.info;
-               if (diva_didd_add_descriptor((DESCRIPTOR *)pinfo->descriptor) < 0) {
-                       e->Rc = OUT_OF_RESOURCES;
-               } else {
-                       e->Rc = 0xff;
-               }
-       } break;
-       case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER: {
-               diva_didd_remove_adapter_t *pinfo = &syncReq->didd_remove_adapter.info;
-               if (diva_didd_remove_descriptor((IDI_CALL)pinfo->p_request) < 0) {
-                       e->Rc = OUT_OF_RESOURCES;
-               } else {
-                       e->Rc = 0xff;
-               }
-       } break;
-       case IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY: {
-               diva_didd_read_adapter_array_t *pinfo =\
-                       &syncReq->didd_read_adapter_array.info;
-               if (diva_didd_read_adapter_array((DESCRIPTOR *)pinfo->buffer,
-                                                 (int)pinfo->length)) {
-                       e->Rc = OUT_OF_RESOURCES;
-               } else {
-                       e->Rc = 0xff;
-               }
-       } break;
-       default:
-               DBG_ERR(("Can't process sync request, Req=%02x", e->Rc))
-                       e->Rc = OUT_OF_RESOURCES;
-       }
-}
-/* --------------------------------------------------------------------------
-   IDI client does register his notification function
-   -------------------------------------------------------------------------- */
-static dword diva_register_adapter_callback(           \
-       didd_adapter_change_callback_t callback,
-       void IDI_CALL_ENTITY_T *context) {
-       diva_os_spin_lock_magic_t irql;
-       dword i;
-
-       for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
-               diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_add");
-               if (!NotificationTable[i].callback) {
-                       NotificationTable[i].callback = callback;
-                       NotificationTable[i].context = context;
-                       diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add");
-                       DBG_TRC(("Register adapter notification[%d]=%08x", i + 1, callback))
-                               return (i + 1);
-               }
-               diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_add");
-       }
-       DBG_ERR(("Can't register adapter notification, overflow"))
-               return (0);
-}
-/* --------------------------------------------------------------------------
-   IDI client does register his notification function
-   -------------------------------------------------------------------------- */
-static void diva_remove_adapter_callback(dword handle) {
-       diva_os_spin_lock_magic_t irql;
-       if (handle && ((--handle) < DIVA_DIDD_MAX_NOTIFICATIONS)) {
-               diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy_rm");
-               NotificationTable[handle].callback = NULL;
-               NotificationTable[handle].context  = NULL;
-               diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy_rm");
-               DBG_TRC(("Remove adapter notification[%d]", (int)(handle + 1)))
-                       return;
-       }
-       DBG_ERR(("Can't remove adapter notification, handle=%d", handle))
-               }
-/* --------------------------------------------------------------------------
-   Notify all client about adapter array change
-   Does suppose following behavior in the client side:
-   Step 1: Redister Notification
-   Step 2: Read Adapter Array
-   -------------------------------------------------------------------------- */
-static void diva_notify_adapter_change(DESCRIPTOR *d, int removal) {
-       int i, do_notify;
-       didd_adapter_change_notification_t nfy;
-       diva_os_spin_lock_magic_t irql;
-       for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
-               do_notify = 0;
-               diva_os_enter_spin_lock(&didd_spin, &irql, "didd_nfy");
-               if (NotificationTable[i].callback) {
-                       memcpy(&nfy, &NotificationTable[i], sizeof(nfy));
-                       do_notify = 1;
-               }
-               diva_os_leave_spin_lock(&didd_spin, &irql, "didd_nfy");
-               if (do_notify) {
-                       (*(nfy.callback))(nfy.context, d, removal);
-               }
-       }
-}
-/* --------------------------------------------------------------------------
-   For all systems, that are linked by Kernel Mode Linker this is ONLY one
-   function thet should be exported by this device driver
-   IDI clients should look for IDI_DADAPTER, and use request function
-   of this adapter (sync request) in order to receive appropriate services:
-   - add new adapter
-   - remove existing adapter
-   - add adapter array notification
-   - remove adapter array notification
-   (read adapter is redundant in this case)
-   INPUT:
-   buffer - pointer to buffer that will receive adapter array
-   length  - length (in bytes) of space in buffer
-   OUTPUT:
-   Adapter array will be written to memory described by 'buffer'
-   If the last adapter seen in the returned adapter array is
-   IDI_DADAPTER or if last adapter in array does have type '0', then
-   it was enougth space in buffer to accommodate all available
-   adapter descriptors
-   *NOTE 1 (debug interface):
-   The IDI adapter of type 'IDI_DIMAINT' does register as 'request'
-   famous 'dprintf' function (of type DI_PRINTF, please look
-   include/debuglib.c and include/debuglib.h) for details.
-   So dprintf is not exported from module debug module directly,
-   instead of this IDI_DIMAINT is registered.
-   Module load order will receive in this case:
-   1. DIDD (this file)
-   2. DIMAINT does load and register 'IDI_DIMAINT', at this step
-   DIDD should be able to get 'dprintf', save it, and
-   register with DIDD by means of 'dprintf' function.
-   3. any other driver is loaded and is able to access adapter array
-   and debug interface
-   This approach does allow to load/unload debug interface on demand,
-   and save memory, it it is necessary.
-   -------------------------------------------------------------------------- */
-void IDI_CALL_LINK_T DIVA_DIDD_Read(void IDI_CALL_ENTITY_T *buffer,
-                                   int length) {
-       diva_didd_read_adapter_array(buffer, length);
-}
diff --git a/drivers/isdn/hardware/eicon/dadapter.h b/drivers/isdn/hardware/eicon/dadapter.h
deleted file mode 100644 (file)
index 5540f46..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_DIDD_DADAPTER_INC__
-#define __DIVA_DIDD_DADAPTER_INC__
-
-void diva_didd_load_time_init(void);
-void diva_didd_load_time_finit(void);
-
-#define NEW_MAX_DESCRIPTORS     64
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c
deleted file mode 100644 (file)
index 3017881..0000000
+++ /dev/null
@@ -1,2128 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include "platform.h"
-#include "pc.h"
-#include "di_defs.h"
-#include "debug_if.h"
-#include "divasync.h"
-#include "kst_ifc.h"
-#include "maintidi.h"
-#include "man_defs.h"
-
-/*
-  LOCALS
-*/
-#define DBG_MAGIC (0x47114711L)
-
-static void DI_register(void *arg);
-static void DI_deregister(pDbgHandle hDbg);
-static void DI_format(int do_lock, word id, int type, char *format, va_list argument_list);
-static void DI_format_locked(word id, int type, char *format, va_list argument_list);
-static void DI_format_old(word id, char *format, va_list ap) { }
-static void DiProcessEventLog(unsigned short id, unsigned long msgID, va_list ap) { }
-static void single_p(byte *P, word *PLength, byte Id);
-static void diva_maint_xdi_cb(ENTITY *e);
-static word SuperTraceCreateReadReq(byte *P, const char *path);
-static int diva_mnt_cmp_nmbr(const char *nmbr);
-static void diva_free_dma_descriptor(IDI_CALL request, int nr);
-static int diva_get_dma_descriptor(IDI_CALL request, dword *dma_magic);
-void diva_mnt_internal_dprintf(dword drv_id, dword type, char *p, ...);
-
-static dword MaxDumpSize = 256;
-static dword MaxXlogSize = 2 + 128;
-static char  TraceFilter[DIVA_MAX_SELECTIVE_FILTER_LENGTH + 1];
-static int TraceFilterIdent   = -1;
-static int TraceFilterChannel = -1;
-
-typedef struct _diva_maint_client {
-       dword       sec;
-       dword       usec;
-       pDbgHandle  hDbg;
-       char        drvName[128];
-       dword       dbgMask;
-       dword       last_dbgMask;
-       IDI_CALL    request;
-       _DbgHandle_ Dbg;
-       int         logical;
-       int         channels;
-       diva_strace_library_interface_t *pIdiLib;
-       BUFFERS     XData;
-       char        xbuffer[2048 + 512];
-       byte        *pmem;
-       int         request_pending;
-       int         dma_handle;
-} diva_maint_client_t;
-static diva_maint_client_t clients[MAX_DESCRIPTORS];
-
-static void diva_change_management_debug_mask(diva_maint_client_t *pC, dword old_mask);
-
-static void diva_maint_error(void *user_context,
-                            diva_strace_library_interface_t *hLib,
-                            int Adapter,
-                            int error,
-                            const char *file,
-                            int line);
-static void diva_maint_state_change_notify(void *user_context,
-                                          diva_strace_library_interface_t *hLib,
-                                          int Adapter,
-                                          diva_trace_line_state_t *channel,
-                                          int notify_subject);
-static void diva_maint_trace_notify(void *user_context,
-                                   diva_strace_library_interface_t *hLib,
-                                   int Adapter,
-                                   void *xlog_buffer,
-                                   int length);
-
-
-
-typedef struct MSG_QUEUE {
-       dword   Size;           /* total size of queue (constant)       */
-       byte    *Base;          /* lowest address (constant)            */
-       byte    *High;          /* Base + Size (constant)               */
-       byte    *Head;          /* first message in queue (if any)      */
-       byte    *Tail;          /* first free position                  */
-       byte    *Wrap;          /* current wraparound position          */
-       dword   Count;          /* current no of bytes in queue         */
-} MSG_QUEUE;
-
-typedef struct MSG_HEAD {
-       volatile dword  Size;           /* size of data following MSG_HEAD      */
-#define        MSG_INCOMPLETE  0x8000  /* ored to Size until queueCompleteMsg  */
-} MSG_HEAD;
-
-#define queueCompleteMsg(p) do { ((MSG_HEAD *)p - 1)->Size &= ~MSG_INCOMPLETE; } while (0)
-#define queueCount(q)  ((q)->Count)
-#define MSG_NEED(size)                                                 \
-       ((sizeof(MSG_HEAD) + size + sizeof(dword) - 1) & ~(sizeof(dword) - 1))
-
-static void queueInit(MSG_QUEUE *Q, byte *Buffer, dword sizeBuffer) {
-       Q->Size = sizeBuffer;
-       Q->Base = Q->Head = Q->Tail = Buffer;
-       Q->High = Buffer + sizeBuffer;
-       Q->Wrap = NULL;
-       Q->Count = 0;
-}
-
-static byte *queueAllocMsg(MSG_QUEUE *Q, word size) {
-       /* Allocate 'size' bytes at tail of queue which will be filled later
-        * directly with callers own message header info and/or message.
-        * An 'alloced' message is marked incomplete by oring the 'Size' field
-        * with MSG_INCOMPLETE.
-        * This must be reset via queueCompleteMsg() after the message is filled.
-        * As long as a message is marked incomplete queuePeekMsg() will return
-        * a 'queue empty' condition when it reaches such a message.  */
-
-       MSG_HEAD *Msg;
-       word need = MSG_NEED(size);
-
-       if (Q->Tail == Q->Head) {
-               if (Q->Wrap || need > Q->Size) {
-                       return NULL; /* full */
-               }
-               goto alloc; /* empty */
-       }
-
-       if (Q->Tail > Q->Head) {
-               if (Q->Tail + need <= Q->High) goto alloc; /* append */
-               if (Q->Base + need > Q->Head) {
-                       return NULL; /* too much */
-               }
-               /* wraparound the queue (but not the message) */
-               Q->Wrap = Q->Tail;
-               Q->Tail = Q->Base;
-               goto alloc;
-       }
-
-       if (Q->Tail + need > Q->Head) {
-               return NULL; /* too much */
-       }
-
-alloc:
-       Msg = (MSG_HEAD *)Q->Tail;
-
-       Msg->Size = size | MSG_INCOMPLETE;
-
-       Q->Tail  += need;
-       Q->Count += size;
-
-
-
-       return ((byte *)(Msg + 1));
-}
-
-static void queueFreeMsg(MSG_QUEUE *Q) {
-/* Free the message at head of queue */
-
-       word size = ((MSG_HEAD *)Q->Head)->Size & ~MSG_INCOMPLETE;
-
-       Q->Head  += MSG_NEED(size);
-       Q->Count -= size;
-
-       if (Q->Wrap) {
-               if (Q->Head >= Q->Wrap) {
-                       Q->Head = Q->Base;
-                       Q->Wrap = NULL;
-               }
-       } else if (Q->Head >= Q->Tail) {
-               Q->Head = Q->Tail = Q->Base;
-       }
-}
-
-static byte *queuePeekMsg(MSG_QUEUE *Q, word *size) {
-       /* Show the first valid message in queue BUT DON'T free the message.
-        * After looking on the message contents it can be freed queueFreeMsg()
-        * or simply remain in message queue.  */
-
-       MSG_HEAD *Msg = (MSG_HEAD *)Q->Head;
-
-       if (((byte *)Msg == Q->Tail && !Q->Wrap) ||
-           (Msg->Size & MSG_INCOMPLETE)) {
-               return NULL;
-       } else {
-               *size = Msg->Size;
-               return ((byte *)(Msg + 1));
-       }
-}
-
-/*
-  Message queue header
-*/
-static MSG_QUEUE *dbg_queue;
-static byte *dbg_base;
-static int                 external_dbg_queue;
-static diva_os_spin_lock_t dbg_q_lock;
-static diva_os_spin_lock_t dbg_adapter_lock;
-static int                 dbg_q_busy;
-static volatile dword      dbg_sequence;
-
-/*
-  INTERFACE:
-  Initialize run time queue structures.
-  base:    base of the message queue
-  length:  length of the message queue
-  do_init: perfor queue reset
-
-  return:  zero on success, -1 on error
-*/
-int diva_maint_init(byte *base, unsigned long length, int do_init) {
-       if (dbg_queue || (!base) || (length < (4096 * 4))) {
-               return (-1);
-       }
-
-       TraceFilter[0]     =  0;
-       TraceFilterIdent   = -1;
-       TraceFilterChannel = -1;
-
-       dbg_base = base;
-
-       *(dword *)base  = (dword)DBG_MAGIC; /* Store Magic */
-       base   += sizeof(dword);
-       length -= sizeof(dword);
-
-       *(dword *)base = 2048; /* Extension Field Length */
-       base   += sizeof(dword);
-       length -= sizeof(dword);
-
-       strcpy(base, "KERNEL MODE BUFFER\n");
-       base   += 2048;
-       length -= 2048;
-
-       *(dword *)base = 0; /* Terminate extension */
-       base   += sizeof(dword);
-       length -= sizeof(dword);
-
-       *(void **)base  =  (void *)(base + sizeof(void *)); /* Store Base  */
-       base   += sizeof(void *);
-       length -= sizeof(void *);
-
-       dbg_queue = (MSG_QUEUE *)base;
-       queueInit(dbg_queue, base + sizeof(MSG_QUEUE), length - sizeof(MSG_QUEUE) - 512);
-       external_dbg_queue = 0;
-
-       if (!do_init) {
-               external_dbg_queue = 1; /* memory was located on the external device */
-       }
-
-
-       if (diva_os_initialize_spin_lock(&dbg_q_lock, "dbg_init")) {
-               dbg_queue = NULL;
-               dbg_base = NULL;
-               external_dbg_queue = 0;
-               return (-1);
-       }
-
-       if (diva_os_initialize_spin_lock(&dbg_adapter_lock, "dbg_init")) {
-               diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_init");
-               dbg_queue = NULL;
-               dbg_base = NULL;
-               external_dbg_queue = 0;
-               return (-1);
-       }
-
-       return (0);
-}
-
-/*
-  INTERFACE:
-  Finit at unload time
-  return address of internal queue or zero if queue
-  was external
-*/
-void *diva_maint_finit(void) {
-       void *ret = (void *)dbg_base;
-       int i;
-
-       dbg_queue = NULL;
-       dbg_base  = NULL;
-
-       if (ret) {
-               diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_finit");
-               diva_os_destroy_spin_lock(&dbg_adapter_lock, "dbg_finit");
-       }
-
-       if (external_dbg_queue) {
-               ret = NULL;
-       }
-       external_dbg_queue = 0;
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].pmem) {
-                       diva_os_free(0, clients[i].pmem);
-               }
-       }
-
-       return (ret);
-}
-
-/*
-  INTERFACE:
-  Return amount of messages in debug queue
-*/
-dword diva_dbg_q_length(void) {
-       return (dbg_queue ? queueCount(dbg_queue)       : 0);
-}
-
-/*
-  INTERFACE:
-  Lock message queue and return the pointer to the first
-  entry.
-*/
-diva_dbg_entry_head_t *diva_maint_get_message(word *size,
-                                             diva_os_spin_lock_magic_t *old_irql) {
-       diva_dbg_entry_head_t *pmsg = NULL;
-
-       diva_os_enter_spin_lock(&dbg_q_lock, old_irql, "read");
-       if (dbg_q_busy) {
-               diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_busy");
-               return NULL;
-       }
-       dbg_q_busy = 1;
-
-       if (!(pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, size))) {
-               dbg_q_busy = 0;
-               diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_empty");
-       }
-
-       return (pmsg);
-}
-
-/*
-  INTERFACE:
-  acknowledge last message and unlock queue
-*/
-void diva_maint_ack_message(int do_release,
-                           diva_os_spin_lock_magic_t *old_irql) {
-       if (!dbg_q_busy) {
-               return;
-       }
-       if (do_release) {
-               queueFreeMsg(dbg_queue);
-       }
-       dbg_q_busy = 0;
-       diva_os_leave_spin_lock(&dbg_q_lock, old_irql, "read_ack");
-}
-
-
-/*
-  INTERFACE:
-  PRT COMP function used to register
-  with MAINT adapter or log in compatibility
-  mode in case older driver version is connected too
-*/
-void diva_maint_prtComp(char *format, ...) {
-       void    *hDbg;
-       va_list ap;
-
-       if (!format)
-               return;
-
-       va_start(ap, format);
-
-       /*
-         register to new log driver functions
-       */
-       if ((format[0] == 0) && ((unsigned char)format[1] == 255)) {
-               hDbg = va_arg(ap, void *); /* ptr to DbgHandle */
-               DI_register(hDbg);
-       }
-
-       va_end(ap);
-}
-
-static void DI_register(void *arg) {
-       diva_os_spin_lock_magic_t old_irql;
-       dword sec, usec;
-       pDbgHandle      hDbg;
-       int id, free_id = -1, best_id = 0;
-
-       diva_os_get_time(&sec, &usec);
-
-       hDbg = (pDbgHandle)arg;
-       /*
-         Check for bad args, specially for the old obsolete debug handle
-       */
-       if ((hDbg == NULL) ||
-           ((hDbg->id == 0) && (((_OldDbgHandle_ *)hDbg)->id == -1)) ||
-           (hDbg->Registered != 0)) {
-               return;
-       }
-
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "register");
-
-       for (id = 1; id < ARRAY_SIZE(clients); id++) {
-               if (clients[id].hDbg == hDbg) {
-                       /*
-                         driver already registered
-                       */
-                       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-                       return;
-               }
-               if (clients[id].hDbg) { /* slot is busy */
-                       continue;
-               }
-               free_id = id;
-               if (!strcmp(clients[id].drvName, hDbg->drvName)) {
-                       /*
-                         This driver was already registered with this name
-                         and slot is still free - reuse it
-                       */
-                       best_id = 1;
-                       break;
-               }
-               if (!clients[id].hDbg) { /* slot is busy */
-                       break;
-               }
-       }
-
-       if (free_id != -1) {
-               diva_dbg_entry_head_t *pmsg = NULL;
-               int len;
-               char tmp[256];
-               word size;
-
-               /*
-                 Register new driver with id == free_id
-               */
-               clients[free_id].hDbg = hDbg;
-               clients[free_id].sec  = sec;
-               clients[free_id].usec = usec;
-               strcpy(clients[free_id].drvName, hDbg->drvName);
-
-               clients[free_id].dbgMask = hDbg->dbgMask;
-               if (best_id) {
-                       hDbg->dbgMask |= clients[free_id].last_dbgMask;
-               } else {
-                       clients[free_id].last_dbgMask = 0;
-               }
-
-               hDbg->Registered = DBG_HANDLE_REG_NEW;
-               hDbg->id         = (byte)free_id;
-               hDbg->dbg_end    = DI_deregister;
-               hDbg->dbg_prt    = DI_format_locked;
-               hDbg->dbg_ev     = DiProcessEventLog;
-               hDbg->dbg_irq    = DI_format_locked;
-               if (hDbg->Version > 0) {
-                       hDbg->dbg_old  = DI_format_old;
-               }
-               hDbg->next       = (pDbgHandle)DBG_MAGIC;
-
-               /*
-                 Log driver register, MAINT driver ID is '0'
-               */
-               len = sprintf(tmp, "DIMAINT - drv # %d = '%s' registered",
-                             free_id, hDbg->drvName);
-
-               while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                      (word)(len + 1 + sizeof(*pmsg))))) {
-                       if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                               queueFreeMsg(dbg_queue);
-                       } else {
-                               break;
-                       }
-               }
-
-               if (pmsg) {
-                       pmsg->sequence    = dbg_sequence++;
-                       pmsg->time_sec    = sec;
-                       pmsg->time_usec   = usec;
-                       pmsg->facility    = MSG_TYPE_STRING;
-                       pmsg->dli         = DLI_REG;
-                       pmsg->drv_id      = 0; /* id 0 - DIMAINT */
-                       pmsg->di_cpu      = 0;
-                       pmsg->data_length = len + 1;
-
-                       memcpy(&pmsg[1], tmp, len + 1);
-                       queueCompleteMsg(pmsg);
-                       diva_maint_wakeup_read();
-               }
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-}
-
-static void DI_deregister(pDbgHandle hDbg) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       dword sec, usec;
-       int i;
-       word size;
-       byte *pmem = NULL;
-
-       diva_os_get_time(&sec, &usec);
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "read");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read");
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].hDbg == hDbg) {
-                       diva_dbg_entry_head_t *pmsg;
-                       char tmp[256];
-                       int len;
-
-                       clients[i].hDbg = NULL;
-
-                       hDbg->id       = -1;
-                       hDbg->dbgMask  = 0;
-                       hDbg->dbg_end  = NULL;
-                       hDbg->dbg_prt  = NULL;
-                       hDbg->dbg_irq  = NULL;
-                       if (hDbg->Version > 0)
-                               hDbg->dbg_old = NULL;
-                       hDbg->Registered = 0;
-                       hDbg->next     = NULL;
-
-                       if (clients[i].pIdiLib) {
-                               (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
-                               clients[i].pIdiLib = NULL;
-
-                               pmem = clients[i].pmem;
-                               clients[i].pmem = NULL;
-                       }
-
-                       /*
-                         Log driver register, MAINT driver ID is '0'
-                       */
-                       len = sprintf(tmp, "DIMAINT - drv # %d = '%s' de-registered",
-                                     i, hDbg->drvName);
-
-                       while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                             (word)(len + 1 + sizeof(*pmsg))))) {
-                               if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                                       queueFreeMsg(dbg_queue);
-                               } else {
-                                       break;
-                               }
-                       }
-
-                       if (pmsg) {
-                               pmsg->sequence    = dbg_sequence++;
-                               pmsg->time_sec    = sec;
-                               pmsg->time_usec   = usec;
-                               pmsg->facility    = MSG_TYPE_STRING;
-                               pmsg->dli         = DLI_REG;
-                               pmsg->drv_id      = 0; /* id 0 - DIMAINT */
-                               pmsg->di_cpu      = 0;
-                               pmsg->data_length = len + 1;
-
-                               memcpy(&pmsg[1], tmp, len + 1);
-                               queueCompleteMsg(pmsg);
-                               diva_maint_wakeup_read();
-                       }
-
-                       break;
-               }
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_ack");
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "read_ack");
-
-       if (pmem) {
-               diva_os_free(0, pmem);
-       }
-}
-
-static void DI_format_locked(unsigned short id,
-                            int type,
-                            char *format,
-                            va_list argument_list) {
-       DI_format(1, id, type, format, argument_list);
-}
-
-static void DI_format(int do_lock,
-                     unsigned short id,
-                     int type,
-                     char *format,
-                     va_list ap) {
-       diva_os_spin_lock_magic_t old_irql;
-       dword sec, usec;
-       diva_dbg_entry_head_t *pmsg = NULL;
-       dword length;
-       word size;
-       static char fmtBuf[MSG_FRAME_MAX_SIZE + sizeof(*pmsg) + 1];
-       char          *data;
-       unsigned short code;
-
-       if (diva_os_in_irq()) {
-               dbg_sequence++;
-               return;
-       }
-
-       if ((!format) ||
-           ((TraceFilter[0] != 0) && ((TraceFilterIdent < 0) || (TraceFilterChannel < 0)))) {
-               return;
-       }
-
-
-
-       diva_os_get_time(&sec, &usec);
-
-       if (do_lock) {
-               diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "format");
-       }
-
-       switch (type) {
-       case DLI_MXLOG:
-       case DLI_BLK:
-       case DLI_SEND:
-       case DLI_RECV:
-               if (!(length = va_arg(ap, unsigned long))) {
-                       break;
-               }
-               if (length > MaxDumpSize) {
-                       length = MaxDumpSize;
-               }
-               while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                      (word)length + sizeof(*pmsg)))) {
-                       if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                               queueFreeMsg(dbg_queue);
-                       } else {
-                               break;
-                       }
-               }
-               if (pmsg) {
-                       memcpy(&pmsg[1], format, length);
-                       pmsg->sequence    = dbg_sequence++;
-                       pmsg->time_sec    = sec;
-                       pmsg->time_usec   = usec;
-                       pmsg->facility    = MSG_TYPE_BINARY;
-                       pmsg->dli         = type; /* DLI_XXX */
-                       pmsg->drv_id      = id;   /* driver MAINT id */
-                       pmsg->di_cpu      = 0;
-                       pmsg->data_length = length;
-                       queueCompleteMsg(pmsg);
-               }
-               break;
-
-       case DLI_XLOG: {
-               byte *p;
-               data    = va_arg(ap, char *);
-               code    = (unsigned short)va_arg(ap, unsigned int);
-               length  = (unsigned long)va_arg(ap, unsigned int);
-
-               if (length > MaxXlogSize)
-                       length = MaxXlogSize;
-
-               while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                     (word)length + sizeof(*pmsg) + 2))) {
-                       if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                               queueFreeMsg(dbg_queue);
-                       } else {
-                               break;
-                       }
-               }
-               if (pmsg) {
-                       p = (byte *)&pmsg[1];
-                       p[0] = (char)(code);
-                       p[1] = (char)(code >> 8);
-                       if (data && length) {
-                               memcpy(&p[2], &data[0], length);
-                       }
-                       length += 2;
-
-                       pmsg->sequence    = dbg_sequence++;
-                       pmsg->time_sec    = sec;
-                       pmsg->time_usec   = usec;
-                       pmsg->facility    = MSG_TYPE_BINARY;
-                       pmsg->dli         = type; /* DLI_XXX */
-                       pmsg->drv_id      = id;   /* driver MAINT id */
-                       pmsg->di_cpu      = 0;
-                       pmsg->data_length = length;
-                       queueCompleteMsg(pmsg);
-               }
-       } break;
-
-       case DLI_LOG:
-       case DLI_FTL:
-       case DLI_ERR:
-       case DLI_TRC:
-       case DLI_REG:
-       case DLI_MEM:
-       case DLI_SPL:
-       case DLI_IRP:
-       case DLI_TIM:
-       case DLI_TAPI:
-       case DLI_NDIS:
-       case DLI_CONN:
-       case DLI_STAT:
-       case DLI_PRV0:
-       case DLI_PRV1:
-       case DLI_PRV2:
-       case DLI_PRV3:
-               if ((length = (unsigned long)vsprintf(&fmtBuf[0], format, ap)) > 0) {
-                       length += (sizeof(*pmsg) + 1);
-
-                       while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                              (word)length))) {
-                               if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                                       queueFreeMsg(dbg_queue);
-                               } else {
-                                       break;
-                               }
-                       }
-
-                       pmsg->sequence    = dbg_sequence++;
-                       pmsg->time_sec    = sec;
-                       pmsg->time_usec   = usec;
-                       pmsg->facility    = MSG_TYPE_STRING;
-                       pmsg->dli         = type; /* DLI_XXX */
-                       pmsg->drv_id      = id;   /* driver MAINT id */
-                       pmsg->di_cpu      = 0;
-                       pmsg->data_length = length - sizeof(*pmsg);
-
-                       memcpy(&pmsg[1], fmtBuf, pmsg->data_length);
-                       queueCompleteMsg(pmsg);
-               }
-               break;
-
-       } /* switch type */
-
-
-       if (queueCount(dbg_queue)) {
-               diva_maint_wakeup_read();
-       }
-
-       if (do_lock) {
-               diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "format");
-       }
-}
-
-/*
-  Write driver ID and driver revision to callers buffer
-*/
-int diva_get_driver_info(dword id, byte *data, int data_length) {
-       diva_os_spin_lock_magic_t old_irql;
-       byte *p = data;
-       int to_copy;
-
-       if (!data || !id || (data_length < 17) ||
-           (id >= ARRAY_SIZE(clients))) {
-               return (-1);
-       }
-
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "driver info");
-
-       if (clients[id].hDbg) {
-               *p++ = 1;
-               *p++ = (byte)clients[id].sec; /* save seconds */
-               *p++ = (byte)(clients[id].sec >>  8);
-               *p++ = (byte)(clients[id].sec >> 16);
-               *p++ = (byte)(clients[id].sec >> 24);
-
-               *p++ = (byte)(clients[id].usec / 1000); /* save mseconds */
-               *p++ = (byte)((clients[id].usec / 1000) >>  8);
-               *p++ = (byte)((clients[id].usec / 1000) >> 16);
-               *p++ = (byte)((clients[id].usec / 1000) >> 24);
-
-               data_length -= 9;
-
-               if ((to_copy = min(strlen(clients[id].drvName), (size_t)(data_length - 1)))) {
-                       memcpy(p, clients[id].drvName, to_copy);
-                       p += to_copy;
-                       data_length -= to_copy;
-                       if ((data_length >= 4) && clients[id].hDbg->drvTag[0]) {
-                               *p++ = '(';
-                               data_length -= 1;
-                               if ((to_copy = min(strlen(clients[id].hDbg->drvTag), (size_t)(data_length - 2)))) {
-                                       memcpy(p, clients[id].hDbg->drvTag, to_copy);
-                                       p += to_copy;
-                                       data_length -= to_copy;
-                                       if (data_length >= 2) {
-                                               *p++ = ')';
-                                               data_length--;
-                                       }
-                               }
-                       }
-               }
-       }
-       *p++ = 0;
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "driver info");
-
-       return (p - data);
-}
-
-int diva_get_driver_dbg_mask(dword id, byte *data) {
-       diva_os_spin_lock_magic_t old_irql;
-       int ret = -1;
-
-       if (!data || !id || (id >= ARRAY_SIZE(clients))) {
-               return (-1);
-       }
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "driver info");
-
-       if (clients[id].hDbg) {
-               ret = 4;
-               *data++ = (byte)(clients[id].hDbg->dbgMask);
-               *data++ = (byte)(clients[id].hDbg->dbgMask >>  8);
-               *data++ = (byte)(clients[id].hDbg->dbgMask >> 16);
-               *data++ = (byte)(clients[id].hDbg->dbgMask >> 24);
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "driver info");
-
-       return (ret);
-}
-
-int diva_set_driver_dbg_mask(dword id, dword mask) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       int ret = -1;
-
-
-       if (!id || (id >= ARRAY_SIZE(clients))) {
-               return (-1);
-       }
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "dbg mask");
-
-       if (clients[id].hDbg) {
-               dword old_mask = clients[id].hDbg->dbgMask;
-               mask &= 0x7fffffff;
-               clients[id].hDbg->dbgMask = mask;
-               clients[id].last_dbgMask = (clients[id].hDbg->dbgMask | clients[id].dbgMask);
-               ret = 4;
-               diva_change_management_debug_mask(&clients[id], old_mask);
-       }
-
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "dbg mask");
-
-       if (clients[id].request_pending) {
-               clients[id].request_pending = 0;
-               (*(clients[id].request))((ENTITY *)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
-       }
-
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask");
-
-       return (ret);
-}
-
-static int diva_get_idi_adapter_info(IDI_CALL request, dword *serial, dword *logical) {
-       IDI_SYNC_REQ sync_req;
-
-       sync_req.xdi_logical_adapter_number.Req = 0;
-       sync_req.xdi_logical_adapter_number.Rc = IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
-       (*request)((ENTITY *)&sync_req);
-       *logical = sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
-
-       sync_req.GetSerial.Req = 0;
-       sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
-       sync_req.GetSerial.serial = 0;
-       (*request)((ENTITY *)&sync_req);
-       *serial = sync_req.GetSerial.serial;
-
-       return (0);
-}
-
-/*
-  Register XDI adapter as MAINT compatible driver
-*/
-void diva_mnt_add_xdi_adapter(const DESCRIPTOR *d) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       dword sec, usec, logical, serial, org_mask;
-       int id, free_id = -1;
-       char tmp[128];
-       diva_dbg_entry_head_t *pmsg = NULL;
-       int len;
-       word size;
-       byte *pmem;
-
-       diva_os_get_time(&sec, &usec);
-       diva_get_idi_adapter_info(d->request, &serial, &logical);
-       if (serial & 0xff000000) {
-               sprintf(tmp, "ADAPTER:%d SN:%u-%d",
-                       (int)logical,
-                       serial & 0x00ffffff,
-                       (byte)(((serial & 0xff000000) >> 24) + 1));
-       } else {
-               sprintf(tmp, "ADAPTER:%d SN:%u", (int)logical, serial);
-       }
-
-       if (!(pmem = diva_os_malloc(0, DivaSTraceGetMemotyRequirement(d->channels)))) {
-               return;
-       }
-       memset(pmem, 0x00, DivaSTraceGetMemotyRequirement(d->channels));
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "register");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "register");
-
-       for (id = 1; id < ARRAY_SIZE(clients); id++) {
-               if (clients[id].hDbg && (clients[id].request == d->request)) {
-                       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-                       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register");
-                       diva_os_free(0, pmem);
-                       return;
-               }
-               if (clients[id].hDbg) { /* slot is busy */
-                       continue;
-               }
-               if (free_id < 0) {
-                       free_id = id;
-               }
-               if (!strcmp(clients[id].drvName, tmp)) {
-                       /*
-                         This driver was already registered with this name
-                         and slot is still free - reuse it
-                       */
-                       free_id = id;
-                       break;
-               }
-       }
-
-       if (free_id < 0) {
-               diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-               diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register");
-               diva_os_free(0, pmem);
-               return;
-       }
-
-       id = free_id;
-       clients[id].request  = d->request;
-       clients[id].request_pending = 0;
-       clients[id].hDbg     = &clients[id].Dbg;
-       clients[id].sec      = sec;
-       clients[id].usec     = usec;
-       strcpy(clients[id].drvName,     tmp);
-       strcpy(clients[id].Dbg.drvName, tmp);
-       clients[id].Dbg.drvTag[0] = 0;
-       clients[id].logical  = (int)logical;
-       clients[id].channels = (int)d->channels;
-       clients[id].dma_handle = -1;
-
-       clients[id].Dbg.dbgMask    = 0;
-       clients[id].dbgMask        = clients[id].Dbg.dbgMask;
-       if (id) {
-               clients[id].Dbg.dbgMask |= clients[free_id].last_dbgMask;
-       } else {
-               clients[id].last_dbgMask = 0;
-       }
-       clients[id].Dbg.Registered = DBG_HANDLE_REG_NEW;
-       clients[id].Dbg.id         = (byte)id;
-       clients[id].Dbg.dbg_end    = DI_deregister;
-       clients[id].Dbg.dbg_prt    = DI_format_locked;
-       clients[id].Dbg.dbg_ev     = DiProcessEventLog;
-       clients[id].Dbg.dbg_irq    = DI_format_locked;
-       clients[id].Dbg.next       = (pDbgHandle)DBG_MAGIC;
-
-       {
-               diva_trace_library_user_interface_t diva_maint_user_ifc = { &clients[id],
-                                                                           diva_maint_state_change_notify,
-                                                                           diva_maint_trace_notify,
-                                                                           diva_maint_error };
-
-               /*
-                 Attach to adapter management interface
-               */
-               if ((clients[id].pIdiLib =
-                    DivaSTraceLibraryCreateInstance((int)logical, &diva_maint_user_ifc, pmem))) {
-                       if (((*(clients[id].pIdiLib->DivaSTraceLibraryStart))(clients[id].pIdiLib->hLib))) {
-                               diva_mnt_internal_dprintf(0, DLI_ERR, "Adapter(%d) Start failed", (int)logical);
-                               (*(clients[id].pIdiLib->DivaSTraceLibraryFinit))(clients[id].pIdiLib->hLib);
-                               clients[id].pIdiLib = NULL;
-                       }
-               } else {
-                       diva_mnt_internal_dprintf(0, DLI_ERR, "A(%d) management init failed", (int)logical);
-               }
-       }
-
-       if (!clients[id].pIdiLib) {
-               clients[id].request = NULL;
-               clients[id].request_pending = 0;
-               clients[id].hDbg    = NULL;
-               diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-               diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register");
-               diva_os_free(0, pmem);
-               return;
-       }
-
-       /*
-         Log driver register, MAINT driver ID is '0'
-       */
-       len = sprintf(tmp, "DIMAINT - drv # %d = '%s' registered",
-                     id, clients[id].Dbg.drvName);
-
-       while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                              (word)(len + 1 + sizeof(*pmsg))))) {
-               if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                       queueFreeMsg(dbg_queue);
-               } else {
-                       break;
-               }
-       }
-
-       if (pmsg) {
-               pmsg->sequence    = dbg_sequence++;
-               pmsg->time_sec    = sec;
-               pmsg->time_usec   = usec;
-               pmsg->facility    = MSG_TYPE_STRING;
-               pmsg->dli         = DLI_REG;
-               pmsg->drv_id      = 0; /* id 0 - DIMAINT */
-               pmsg->di_cpu      = 0;
-               pmsg->data_length = len + 1;
-
-               memcpy(&pmsg[1], tmp, len + 1);
-               queueCompleteMsg(pmsg);
-               diva_maint_wakeup_read();
-       }
-
-       org_mask = clients[id].Dbg.dbgMask;
-       clients[id].Dbg.dbgMask = 0;
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "register");
-
-       if (clients[id].request_pending) {
-               clients[id].request_pending = 0;
-               (*(clients[id].request))((ENTITY *)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
-       }
-
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "register");
-
-       diva_set_driver_dbg_mask(id, org_mask);
-}
-
-/*
-  De-Register XDI adapter
-*/
-void diva_mnt_remove_xdi_adapter(const DESCRIPTOR *d) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       dword sec, usec;
-       int i;
-       word size;
-       byte *pmem = NULL;
-
-       diva_os_get_time(&sec, &usec);
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "read");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read");
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].hDbg && (clients[i].request == d->request)) {
-                       diva_dbg_entry_head_t *pmsg;
-                       char tmp[256];
-                       int len;
-
-                       if (clients[i].pIdiLib) {
-                               (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
-                               clients[i].pIdiLib = NULL;
-
-                               pmem = clients[i].pmem;
-                               clients[i].pmem = NULL;
-                       }
-
-                       clients[i].hDbg    = NULL;
-                       clients[i].request_pending = 0;
-                       if (clients[i].dma_handle >= 0) {
-                               /*
-                                 Free DMA handle
-                               */
-                               diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle);
-                               clients[i].dma_handle = -1;
-                       }
-                       clients[i].request = NULL;
-
-                       /*
-                         Log driver register, MAINT driver ID is '0'
-                       */
-                       len = sprintf(tmp, "DIMAINT - drv # %d = '%s' de-registered",
-                                     i, clients[i].Dbg.drvName);
-
-                       memset(&clients[i].Dbg, 0x00, sizeof(clients[i].Dbg));
-
-                       while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                                              (word)(len + 1 + sizeof(*pmsg))))) {
-                               if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                                       queueFreeMsg(dbg_queue);
-                               } else {
-                                       break;
-                               }
-                       }
-
-                       if (pmsg) {
-                               pmsg->sequence    = dbg_sequence++;
-                               pmsg->time_sec    = sec;
-                               pmsg->time_usec   = usec;
-                               pmsg->facility    = MSG_TYPE_STRING;
-                               pmsg->dli         = DLI_REG;
-                               pmsg->drv_id      = 0; /* id 0 - DIMAINT */
-                               pmsg->di_cpu      = 0;
-                               pmsg->data_length = len + 1;
-
-                               memcpy(&pmsg[1], tmp, len + 1);
-                               queueCompleteMsg(pmsg);
-                               diva_maint_wakeup_read();
-                       }
-
-                       break;
-               }
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_ack");
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "read_ack");
-
-       if (pmem) {
-               diva_os_free(0, pmem);
-       }
-}
-
-/* ----------------------------------------------------------------
-   Low level interface for management interface client
-   ---------------------------------------------------------------- */
-/*
-  Return handle to client structure
-*/
-void *SuperTraceOpenAdapter(int AdapterNumber) {
-       int i;
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].hDbg && clients[i].request && (clients[i].logical == AdapterNumber)) {
-                       return (&clients[i]);
-               }
-       }
-
-       return NULL;
-}
-
-int SuperTraceCloseAdapter(void *AdapterHandle) {
-       return (0);
-}
-
-int SuperTraceReadRequest(void *AdapterHandle, const char *name, byte *data) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-               byte *xdata = (byte *)&pC->xbuffer[0];
-               char tmp = 0;
-               word length;
-
-               if (!strcmp(name, "\\")) { /* Read ROOT */
-                       name = &tmp;
-               }
-               length = SuperTraceCreateReadReq(xdata, name);
-               single_p(xdata, &length, 0); /* End Of Message */
-
-               e->Req        = MAN_READ;
-               e->ReqCh      = 0;
-               e->X->PLength = length;
-               e->X->P = (byte *)xdata;
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-int SuperTraceGetNumberOfChannels(void *AdapterHandle) {
-       if (AdapterHandle) {
-               diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-               return (pC->channels);
-       }
-
-       return (0);
-}
-
-int SuperTraceASSIGN(void *AdapterHandle, byte *data) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-               IDI_SYNC_REQ *preq;
-               char buffer[((sizeof(preq->xdi_extended_features) + 4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features) + 4) : sizeof(ENTITY)];
-               char features[4];
-               word assign_data_length = 1;
-
-               features[0] = 0;
-               pC->xbuffer[0] = 0;
-               preq = (IDI_SYNC_REQ *)&buffer[0];
-               preq->xdi_extended_features.Req = 0;
-               preq->xdi_extended_features.Rc  = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
-               preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
-               preq->xdi_extended_features.info.features = &features[0];
-
-               (*(pC->request))((ENTITY *)preq);
-
-               if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) &&
-                   (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) {
-                       dword uninitialized_var(rx_dma_magic);
-                       if ((pC->dma_handle = diva_get_dma_descriptor(pC->request, &rx_dma_magic)) >= 0) {
-                               pC->xbuffer[0] = LLI;
-                               pC->xbuffer[1] = 8;
-                               pC->xbuffer[2] = 0x40;
-                               pC->xbuffer[3] = (byte)pC->dma_handle;
-                               pC->xbuffer[4] = (byte)rx_dma_magic;
-                               pC->xbuffer[5] = (byte)(rx_dma_magic >>  8);
-                               pC->xbuffer[6] = (byte)(rx_dma_magic >> 16);
-                               pC->xbuffer[7] = (byte)(rx_dma_magic >> 24);
-                               pC->xbuffer[8] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE & 0xFF);
-                               pC->xbuffer[9] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE >> 8);
-                               pC->xbuffer[10] = 0;
-
-                               assign_data_length = 11;
-                       }
-               } else {
-                       pC->dma_handle = -1;
-               }
-
-               e->Id          = MAN_ID;
-               e->callback    = diva_maint_xdi_cb;
-               e->XNum        = 1;
-               e->X           = &pC->XData;
-               e->Req         = ASSIGN;
-               e->ReqCh       = 0;
-               e->X->PLength  = assign_data_length;
-               e->X->P        = (byte *)&pC->xbuffer[0];
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-int SuperTraceREMOVE(void *AdapterHandle) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-
-               e->XNum        = 1;
-               e->X           = &pC->XData;
-               e->Req         = REMOVE;
-               e->ReqCh       = 0;
-               e->X->PLength  = 1;
-               e->X->P        = (byte *)&pC->xbuffer[0];
-               pC->xbuffer[0] = 0;
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-int SuperTraceTraceOnRequest(void *hAdapter, const char *name, byte *data) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)hAdapter;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-               byte *xdata = (byte *)&pC->xbuffer[0];
-               char tmp = 0;
-               word length;
-
-               if (!strcmp(name, "\\")) { /* Read ROOT */
-                       name = &tmp;
-               }
-               length = SuperTraceCreateReadReq(xdata, name);
-               single_p(xdata, &length, 0); /* End Of Message */
-               e->Req          = MAN_EVENT_ON;
-               e->ReqCh        = 0;
-               e->X->PLength   = length;
-               e->X->P = (byte *)xdata;
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-int SuperTraceWriteVar(void *AdapterHandle,
-                      byte *data,
-                      const char *name,
-                      void *var,
-                      byte type,
-                      byte var_length) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-               diva_man_var_header_t *pVar = (diva_man_var_header_t *)&pC->xbuffer[0];
-               word length = SuperTraceCreateReadReq((byte *)pVar, name);
-
-               memcpy(&pC->xbuffer[length], var, var_length);
-               length += var_length;
-               pVar->length += var_length;
-               pVar->value_length = var_length;
-               pVar->type = type;
-               single_p((byte *)pVar, &length, 0); /* End Of Message */
-
-               e->Req = MAN_WRITE;
-               e->ReqCh = 0;
-               e->X->PLength   = length;
-               e->X->P = (byte *)pVar;
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-int SuperTraceExecuteRequest(void *AdapterHandle,
-                            const char *name,
-                            byte *data) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)AdapterHandle;
-
-       if (pC && pC->pIdiLib && pC->request) {
-               ENTITY *e = (ENTITY *)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
-               byte *xdata = (byte *)&pC->xbuffer[0];
-               word length;
-
-               length = SuperTraceCreateReadReq(xdata, name);
-               single_p(xdata, &length, 0); /* End Of Message */
-
-               e->Req = MAN_EXECUTE;
-               e->ReqCh = 0;
-               e->X->PLength = length;
-               e->X->P = (byte *)xdata;
-
-               pC->request_pending = 1;
-
-               return (0);
-       }
-
-       return (-1);
-}
-
-static word SuperTraceCreateReadReq(byte *P, const char *path) {
-       byte var_length;
-       byte *plen;
-
-       var_length = (byte)strlen(path);
-
-       *P++ = ESC;
-       plen = P++;
-       *P++ = 0x80; /* MAN_IE */
-       *P++ = 0x00; /* Type */
-       *P++ = 0x00; /* Attribute */
-       *P++ = 0x00; /* Status */
-       *P++ = 0x00; /* Variable Length */
-       *P++ = var_length;
-       memcpy(P, path, var_length);
-       P += var_length;
-       *plen = var_length + 0x06;
-
-       return ((word)(var_length + 0x08));
-}
-
-static void single_p(byte *P, word *PLength, byte Id) {
-       P[(*PLength)++] = Id;
-}
-
-static void diva_maint_xdi_cb(ENTITY *e) {
-       diva_strace_context_t *pLib = DIVAS_CONTAINING_RECORD(e, diva_strace_context_t, e);
-       diva_maint_client_t *pC;
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "xdi_cb");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "xdi_cb");
-
-       pC = (diva_maint_client_t *)pLib->hAdapter;
-
-       if ((e->complete == 255) || (pC->dma_handle < 0)) {
-               if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
-                       diva_mnt_internal_dprintf(0, DLI_ERR, "Trace internal library error");
-               }
-       } else {
-               /*
-                 Process combined management interface indication
-               */
-               if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
-                       diva_mnt_internal_dprintf(0, DLI_ERR, "Trace internal library error (DMA mode)");
-               }
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "xdi_cb");
-
-
-       if (pC->request_pending) {
-               pC->request_pending = 0;
-               (*(pC->request))(e);
-       }
-
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "xdi_cb");
-}
-
-
-static void diva_maint_error(void *user_context,
-                            diva_strace_library_interface_t *hLib,
-                            int Adapter,
-                            int error,
-                            const char *file,
-                            int line) {
-       diva_mnt_internal_dprintf(0, DLI_ERR,
-                                 "Trace library error(%d) A(%d) %s %d", error, Adapter, file, line);
-}
-
-static void print_ie(diva_trace_ie_t *ie, char *buffer, int length) {
-       int i;
-
-       buffer[0] = 0;
-
-       if (length > 32) {
-               for (i = 0; ((i < ie->length) && (length > 3)); i++) {
-                       sprintf(buffer, "%02x", ie->data[i]);
-                       buffer += 2;
-                       length -= 2;
-                       if (i < (ie->length - 1)) {
-                               strcpy(buffer, " ");
-                               buffer++;
-                               length--;
-                       }
-               }
-       }
-}
-
-static void diva_maint_state_change_notify(void *user_context,
-                                          diva_strace_library_interface_t *hLib,
-                                          int Adapter,
-                                          diva_trace_line_state_t *channel,
-                                          int notify_subject) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)user_context;
-       diva_trace_fax_state_t *fax = &channel->fax;
-       diva_trace_modem_state_t *modem = &channel->modem;
-       char tmp[256];
-
-       if (!pC->hDbg) {
-               return;
-       }
-
-       switch (notify_subject) {
-       case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: {
-               int view = (TraceFilter[0] == 0);
-               /*
-                 Process selective Trace
-               */
-               if (channel->Line[0] == 'I' && channel->Line[1] == 'd' &&
-                   channel->Line[2] == 'l' && channel->Line[3] == 'e') {
-                       if ((TraceFilterIdent == pC->hDbg->id) && (TraceFilterChannel == (int)channel->ChannelNumber)) {
-                               (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 0);
-                               (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 0);
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, "Selective Trace OFF for Ch=%d",
-                                                         (int)channel->ChannelNumber);
-                               TraceFilterIdent   = -1;
-                               TraceFilterChannel = -1;
-                               view = 1;
-                       }
-               } else if (TraceFilter[0] && (TraceFilterIdent < 0) && !(diva_mnt_cmp_nmbr(&channel->RemoteAddress[0]) &&
-                                                                        diva_mnt_cmp_nmbr(&channel->LocalAddress[0]))) {
-
-                       if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0) { /* Activate B-channel trace */
-                               (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 1);
-                       }
-                       if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0) { /* Activate AudioTap Trace */
-                               (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 1);
-                       }
-
-                       TraceFilterIdent   = pC->hDbg->id;
-                       TraceFilterChannel = (int)channel->ChannelNumber;
-
-                       if (TraceFilterIdent >= 0) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG, "Selective Trace ON for Ch=%d",
-                                                         (int)channel->ChannelNumber);
-                               view = 1;
-                       }
-               }
-               if (view && (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS)) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Ch     = %d",
-                                                 (int)channel->ChannelNumber);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Status = <%s>", &channel->Line[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer1 = <%s>", &channel->Framing[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer2 = <%s>", &channel->Layer2[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Layer3 = <%s>", &channel->Layer3[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L RAddr  = <%s>",
-                                                 &channel->RemoteAddress[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L RSAddr = <%s>",
-                                                 &channel->RemoteSubAddress[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LAddr  = <%s>",
-                                                 &channel->LocalAddress[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LSAddr = <%s>",
-                                                 &channel->LocalSubAddress[0]);
-                       print_ie(&channel->call_BC, tmp, sizeof(tmp));
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L BC     = <%s>", tmp);
-                       print_ie(&channel->call_HLC, tmp, sizeof(tmp));
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L HLC    = <%s>", tmp);
-                       print_ie(&channel->call_LLC, tmp, sizeof(tmp));
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L LLC    = <%s>", tmp);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L CR     = 0x%x", channel->CallReference);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Disc   = 0x%x",
-                                                 channel->LastDisconnecCause);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "L Owner  = <%s>", &channel->UserID[0]);
-               }
-
-       } break;
-
-       case DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE:
-               if (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_PROGRESS) {
-                       {
-                               int ch = TraceFilterChannel;
-                               int id = TraceFilterIdent;
-
-                               if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
-                                   (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
-                                       if (ch != (int)modem->ChannelNumber) {
-                                               break;
-                                       }
-                               } else if (TraceFilter[0] != 0) {
-                                       break;
-                               }
-                       }
-
-
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Ch    = %lu",
-                                                 (int)modem->ChannelNumber);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Event = %lu", modem->Event);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Norm  = %lu", modem->Norm);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Opts. = 0x%08x", modem->Options);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Tx    = %lu Bps", modem->TxSpeed);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rx    = %lu Bps", modem->RxSpeed);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RT    = %lu mSec",
-                                                 modem->RoundtripMsec);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Sr    = %lu", modem->SymbolRate);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rxl   = %d dBm", modem->RxLeveldBm);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM El    = %d dBm", modem->EchoLeveldBm);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM SNR   = %lu dB", modem->SNRdb);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM MAE   = %lu", modem->MAE);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRet  = %lu",
-                                                 modem->LocalRetrains);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRet  = %lu",
-                                                 modem->RemoteRetrains);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRes  = %lu", modem->LocalResyncs);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRes  = %lu",
-                                                 modem->RemoteResyncs);
-                       if (modem->Event == 3) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Disc  =  %lu", modem->DiscReason);
-                       }
-               }
-               if ((modem->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_STATISTICS)) {
-                       (*(pC->pIdiLib->DivaSTraceGetModemStatistics))(pC->pIdiLib);
-               }
-               break;
-
-       case DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE:
-               if (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_PROGRESS) {
-                       {
-                               int ch = TraceFilterChannel;
-                               int id = TraceFilterIdent;
-
-                               if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
-                                   (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
-                                       if (ch != (int)fax->ChannelNumber) {
-                                               break;
-                                       }
-                               } else if (TraceFilter[0] != 0) {
-                                       break;
-                               }
-                       }
-
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Ch    = %lu", (int)fax->ChannelNumber);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Event = %lu",     fax->Event);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pages = %lu",     fax->Page_Counter);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Feat. = 0x%08x",  fax->Features);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX ID    = <%s>",    &fax->Station_ID[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Saddr = <%s>",    &fax->Subaddress[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pwd   = <%s>",    &fax->Password[0]);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Speed = %lu",     fax->Speed);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Res.  = 0x%08x",  fax->Resolution);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Width = %lu",     fax->Paper_Width);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Length= %lu",     fax->Paper_Length);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX SLT   = %lu",     fax->Scanline_Time);
-                       if (fax->Event == 3) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Disc  = %lu",     fax->Disc_Reason);
-                       }
-               }
-               if ((fax->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_STATISTICS)) {
-                       (*(pC->pIdiLib->DivaSTraceGetFaxStatistics))(pC->pIdiLib);
-               }
-               break;
-
-       case DIVA_SUPER_TRACE_INTERFACE_CHANGE:
-               if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_EVENTS) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT,
-                                                 "Layer 1 -> [%s]", channel->pInterface->Layer1);
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT,
-                                                 "Layer 2 -> [%s]", channel->pInterface->Layer2);
-               }
-               break;
-
-       case DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE:
-               if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_STATISTICS) {
-                       /*
-                         Incoming Statistics
-                       */
-                       if (channel->pInterfaceStat->inc.Calls) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Calls                     =%lu", channel->pInterfaceStat->inc.Calls);
-                       }
-                       if (channel->pInterfaceStat->inc.Connected) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Connected                 =%lu", channel->pInterfaceStat->inc.Connected);
-                       }
-                       if (channel->pInterfaceStat->inc.User_Busy) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Busy                      =%lu", channel->pInterfaceStat->inc.User_Busy);
-                       }
-                       if (channel->pInterfaceStat->inc.Call_Rejected) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Rejected                  =%lu", channel->pInterfaceStat->inc.Call_Rejected);
-                       }
-                       if (channel->pInterfaceStat->inc.Wrong_Number) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Wrong Nr                  =%lu", channel->pInterfaceStat->inc.Wrong_Number);
-                       }
-                       if (channel->pInterfaceStat->inc.Incompatible_Dst) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Incomp. Dest              =%lu", channel->pInterfaceStat->inc.Incompatible_Dst);
-                       }
-                       if (channel->pInterfaceStat->inc.Out_of_Order) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Out of Order              =%lu", channel->pInterfaceStat->inc.Out_of_Order);
-                       }
-                       if (channel->pInterfaceStat->inc.Ignored) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Inc Ignored                   =%lu", channel->pInterfaceStat->inc.Ignored);
-                       }
-
-                       /*
-                         Outgoing Statistics
-                       */
-                       if (channel->pInterfaceStat->outg.Calls) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Calls                    =%lu", channel->pInterfaceStat->outg.Calls);
-                       }
-                       if (channel->pInterfaceStat->outg.Connected) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Connected                =%lu", channel->pInterfaceStat->outg.Connected);
-                       }
-                       if (channel->pInterfaceStat->outg.User_Busy) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Busy                     =%lu", channel->pInterfaceStat->outg.User_Busy);
-                       }
-                       if (channel->pInterfaceStat->outg.No_Answer) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg No Answer                =%lu", channel->pInterfaceStat->outg.No_Answer);
-                       }
-                       if (channel->pInterfaceStat->outg.Wrong_Number) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Wrong Nr                 =%lu", channel->pInterfaceStat->outg.Wrong_Number);
-                       }
-                       if (channel->pInterfaceStat->outg.Call_Rejected) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Rejected                 =%lu", channel->pInterfaceStat->outg.Call_Rejected);
-                       }
-                       if (channel->pInterfaceStat->outg.Other_Failures) {
-                               diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                         "Outg Other Failures           =%lu", channel->pInterfaceStat->outg.Other_Failures);
-                       }
-               }
-               break;
-
-       case DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE:
-               if (channel->pInterfaceStat->mdm.Disc_Normal) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Normal        = %lu", channel->pInterfaceStat->mdm.Disc_Normal);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Unspecified) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Unsp.         = %lu", channel->pInterfaceStat->mdm.Disc_Unspecified);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Busy_Tone) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Busy Tone     = %lu", channel->pInterfaceStat->mdm.Disc_Busy_Tone);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Congestion) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Congestion    = %lu", channel->pInterfaceStat->mdm.Disc_Congestion);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Carr_Wait) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Carrier Wait  = %lu", channel->pInterfaceStat->mdm.Disc_Carr_Wait);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Trn_Timeout) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Trn. T.o.     = %lu", channel->pInterfaceStat->mdm.Disc_Trn_Timeout);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Incompat) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Incompatible  = %lu", channel->pInterfaceStat->mdm.Disc_Incompat);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_Frame_Rej) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc Frame Reject  = %lu", channel->pInterfaceStat->mdm.Disc_Frame_Rej);
-               }
-               if (channel->pInterfaceStat->mdm.Disc_V42bis) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "MDM Disc V.42bis       = %lu", channel->pInterfaceStat->mdm.Disc_V42bis);
-               }
-               break;
-
-       case DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE:
-               if (channel->pInterfaceStat->fax.Disc_Normal) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Normal        = %lu", channel->pInterfaceStat->fax.Disc_Normal);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Not_Ident) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Not Ident.    = %lu", channel->pInterfaceStat->fax.Disc_Not_Ident);
-               }
-               if (channel->pInterfaceStat->fax.Disc_No_Response) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc No Response   = %lu", channel->pInterfaceStat->fax.Disc_No_Response);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Retries) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Max Retries   = %lu", channel->pInterfaceStat->fax.Disc_Retries);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Unexp_Msg) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Unexp. Msg.        = %lu", channel->pInterfaceStat->fax.Disc_Unexp_Msg);
-               }
-               if (channel->pInterfaceStat->fax.Disc_No_Polling) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc No Polling    = %lu", channel->pInterfaceStat->fax.Disc_No_Polling);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Training) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Training      = %lu", channel->pInterfaceStat->fax.Disc_Training);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Unexpected) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Unexpected    = %lu", channel->pInterfaceStat->fax.Disc_Unexpected);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Application) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Application   = %lu", channel->pInterfaceStat->fax.Disc_Application);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Incompat) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Incompatible  = %lu", channel->pInterfaceStat->fax.Disc_Incompat);
-               }
-               if (channel->pInterfaceStat->fax.Disc_No_Command) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc No Command    = %lu", channel->pInterfaceStat->fax.Disc_No_Command);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Long_Msg) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Long Msg.     = %lu", channel->pInterfaceStat->fax.Disc_Long_Msg);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Supervisor) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Supervisor    = %lu", channel->pInterfaceStat->fax.Disc_Supervisor);
-               }
-               if (channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc SUP SEP PWD   = %lu", channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Invalid_Msg) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Invalid Msg.  = %lu", channel->pInterfaceStat->fax.Disc_Invalid_Msg);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Page_Coding) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Page Coding   = %lu", channel->pInterfaceStat->fax.Disc_Page_Coding);
-               }
-               if (channel->pInterfaceStat->fax.Disc_App_Timeout) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Appl. T.o.    = %lu", channel->pInterfaceStat->fax.Disc_App_Timeout);
-               }
-               if (channel->pInterfaceStat->fax.Disc_Unspecified) {
-                       diva_mnt_internal_dprintf(pC->hDbg->id, DLI_LOG,
-                                                 "FAX Disc Unspec.       = %lu", channel->pInterfaceStat->fax.Disc_Unspecified);
-               }
-               break;
-       }
-}
-
-/*
-  Receive trace information from the Management Interface and store it in the
-  internal trace buffer with MSG_TYPE_MLOG as is, without any filtering.
-  Event Filtering and formatting is done in  Management Interface self.
-*/
-static void diva_maint_trace_notify(void *user_context,
-                                   diva_strace_library_interface_t *hLib,
-                                   int Adapter,
-                                   void *xlog_buffer,
-                                   int length) {
-       diva_maint_client_t *pC = (diva_maint_client_t *)user_context;
-       diva_dbg_entry_head_t *pmsg;
-       word size;
-       dword sec, usec;
-       int ch = TraceFilterChannel;
-       int id = TraceFilterIdent;
-
-       /*
-         Selective trace
-       */
-       if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
-           (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
-               const char *p = NULL;
-               int ch_value = -1;
-               MI_XLOG_HDR *TrcData = (MI_XLOG_HDR *)xlog_buffer;
-
-               if (Adapter != clients[id].logical) {
-                       return; /* Ignore all trace messages from other adapters */
-               }
-
-               if (TrcData->code == 24) {
-                       p = (char *)&TrcData->code;
-                       p += 2;
-               }
-
-               /*
-                 All L1 messages start as [dsp,ch], so we can filter this information
-                 and filter out all messages that use different channel
-               */
-               if (p && p[0] == '[') {
-                       if (p[2] == ',') {
-                               p += 3;
-                               ch_value = *p - '0';
-                       } else if (p[3] == ',') {
-                               p += 4;
-                               ch_value = *p - '0';
-                       }
-                       if (ch_value >= 0) {
-                               if (p[2] == ']') {
-                                       ch_value = ch_value * 10 + p[1] - '0';
-                               }
-                               if (ch_value != ch) {
-                                       return; /* Ignore other channels */
-                               }
-                       }
-               }
-
-       } else if (TraceFilter[0] != 0) {
-               return; /* Ignore trace if trace filter is activated, but idle */
-       }
-
-       diva_os_get_time(&sec, &usec);
-
-       while (!(pmsg = (diva_dbg_entry_head_t *)queueAllocMsg(dbg_queue,
-                                                              (word)length + sizeof(*pmsg)))) {
-               if ((pmsg = (diva_dbg_entry_head_t *)queuePeekMsg(dbg_queue, &size))) {
-                       queueFreeMsg(dbg_queue);
-               } else {
-                       break;
-               }
-       }
-       if (pmsg) {
-               memcpy(&pmsg[1], xlog_buffer, length);
-               pmsg->sequence    = dbg_sequence++;
-               pmsg->time_sec    = sec;
-               pmsg->time_usec   = usec;
-               pmsg->facility    = MSG_TYPE_MLOG;
-               pmsg->dli         = pC->logical;
-               pmsg->drv_id      = pC->hDbg->id;
-               pmsg->di_cpu      = 0;
-               pmsg->data_length = length;
-               queueCompleteMsg(pmsg);
-               if (queueCount(dbg_queue)) {
-                       diva_maint_wakeup_read();
-               }
-       }
-}
-
-
-/*
-  Convert MAINT trace mask to management interface trace mask/work/facility and
-  issue command to management interface
-*/
-static void diva_change_management_debug_mask(diva_maint_client_t *pC, dword old_mask) {
-       if (pC->request && pC->hDbg && pC->pIdiLib) {
-               dword changed = pC->hDbg->dbgMask ^ old_mask;
-
-               if (changed & DIVA_MGT_DBG_TRACE) {
-                       (*(pC->pIdiLib->DivaSTraceSetInfo))(pC->pIdiLib,
-                                                           (pC->hDbg->dbgMask & DIVA_MGT_DBG_TRACE) != 0);
-               }
-               if (changed & DIVA_MGT_DBG_DCHAN) {
-                       (*(pC->pIdiLib->DivaSTraceSetDChannel))(pC->pIdiLib,
-                                                               (pC->hDbg->dbgMask & DIVA_MGT_DBG_DCHAN) != 0);
-               }
-               if (!TraceFilter[0]) {
-                       if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) {
-                               int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
-
-                               for (i = 0; i < pC->channels; i++) {
-                                       (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i + 1, state);
-                               }
-                       }
-                       if (changed & DIVA_MGT_DBG_IFC_AUDIO) {
-                               int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0);
-
-                               for (i = 0; i < pC->channels; i++) {
-                                       (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i + 1, state);
-                               }
-                       }
-               }
-       }
-}
-
-
-void diva_mnt_internal_dprintf(dword drv_id, dword type, char *fmt, ...) {
-       va_list ap;
-
-       va_start(ap, fmt);
-       DI_format(0, (word)drv_id, (int)type, fmt, ap);
-       va_end(ap);
-}
-
-/*
-  Shutdown all adapters before driver removal
-*/
-int diva_mnt_shutdown_xdi_adapters(void) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       int i, fret = 0;
-       byte *pmem;
-
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               pmem = NULL;
-
-               diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "unload");
-               diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "unload");
-
-               if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
-                       if ((*(clients[i].pIdiLib->DivaSTraceLibraryStop))(clients[i].pIdiLib) == 1) {
-                               /*
-                                 Adapter removal complete
-                               */
-                               if (clients[i].pIdiLib) {
-                                       (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
-                                       clients[i].pIdiLib = NULL;
-
-                                       pmem = clients[i].pmem;
-                                       clients[i].pmem = NULL;
-                               }
-                               clients[i].hDbg    = NULL;
-                               clients[i].request_pending = 0;
-
-                               if (clients[i].dma_handle >= 0) {
-                                       /*
-                                         Free DMA handle
-                                       */
-                                       diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle);
-                                       clients[i].dma_handle = -1;
-                               }
-                               clients[i].request = NULL;
-                       } else {
-                               fret = -1;
-                       }
-               }
-
-               diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "unload");
-               if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
-                       clients[i].request_pending = 0;
-                       (*(clients[i].request))((ENTITY *)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
-                       if (clients[i].dma_handle >= 0) {
-                               diva_free_dma_descriptor(clients[i].request, clients[i].dma_handle);
-                               clients[i].dma_handle = -1;
-                       }
-               }
-               diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "unload");
-
-               if (pmem) {
-                       diva_os_free(0, pmem);
-               }
-       }
-
-       return (fret);
-}
-
-/*
-  Set/Read the trace filter used for selective tracing.
-  Affects B- and Audio Tap trace mask at run time
-*/
-int diva_set_trace_filter(int filter_length, const char *filter) {
-       diva_os_spin_lock_magic_t old_irql, old_irql1;
-       int i, ch, on, client_b_on, client_atap_on;
-
-       diva_os_enter_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask");
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "write_filter");
-
-       if (filter_length <= DIVA_MAX_SELECTIVE_FILTER_LENGTH) {
-               memcpy(&TraceFilter[0], filter, filter_length);
-               if (TraceFilter[filter_length]) {
-                       TraceFilter[filter_length] = 0;
-               }
-               if (TraceFilter[0] == '*') {
-                       TraceFilter[0] = 0;
-               }
-       } else {
-               filter_length = -1;
-       }
-
-       TraceFilterIdent   = -1;
-       TraceFilterChannel = -1;
-
-       on = (TraceFilter[0] == 0);
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
-                       client_b_on    = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
-                       client_atap_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO)    != 0);
-                       for (ch = 0; ch < clients[i].channels; ch++) {
-                               (*(clients[i].pIdiLib->DivaSTraceSetBChannel))(clients[i].pIdiLib->hLib, ch + 1, client_b_on);
-                               (*(clients[i].pIdiLib->DivaSTraceSetAudioTap))(clients[i].pIdiLib->hLib, ch + 1, client_atap_on);
-                       }
-               }
-       }
-
-       for (i = 1; i < ARRAY_SIZE(clients); i++) {
-               if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
-                       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "write_filter");
-                       clients[i].request_pending = 0;
-                       (*(clients[i].request))((ENTITY *)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
-                       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "write_filter");
-               }
-       }
-
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "write_filter");
-       diva_os_leave_spin_lock(&dbg_adapter_lock, &old_irql1, "dbg mask");
-
-       return (filter_length);
-}
-
-int diva_get_trace_filter(int max_length, char *filter) {
-       diva_os_spin_lock_magic_t old_irql;
-       int len;
-
-       diva_os_enter_spin_lock(&dbg_q_lock, &old_irql, "read_filter");
-       len = strlen(&TraceFilter[0]) + 1;
-       if (max_length >= len) {
-               memcpy(filter, &TraceFilter[0], len);
-       }
-       diva_os_leave_spin_lock(&dbg_q_lock, &old_irql, "read_filter");
-
-       return (len);
-}
-
-static int diva_dbg_cmp_key(const char *ref, const char *key) {
-       while (*key && (*ref++ == *key++));
-       return (!*key && !*ref);
-}
-
-/*
-  In case trace filter starts with "C" character then
-  all following characters are interpreted as command.
-  Following commands are available:
-  - single, trace single call at time, independent from CPN/CiPN
-*/
-static int diva_mnt_cmp_nmbr(const char *nmbr) {
-       const char *ref = &TraceFilter[0];
-       int ref_len = strlen(&TraceFilter[0]), nmbr_len = strlen(nmbr);
-
-       if (ref[0] == 'C') {
-               if (diva_dbg_cmp_key(&ref[1], "single")) {
-                       return (0);
-               }
-               return (-1);
-       }
-
-       if (!ref_len || (ref_len > nmbr_len)) {
-               return (-1);
-       }
-
-       nmbr = nmbr + nmbr_len - 1;
-       ref  = ref  + ref_len  - 1;
-
-       while (ref_len--) {
-               if (*nmbr-- != *ref--) {
-                       return (-1);
-               }
-       }
-
-       return (0);
-}
-
-static int diva_get_dma_descriptor(IDI_CALL request, dword *dma_magic) {
-       ENTITY e;
-       IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;
-
-       if (!request) {
-               return (-1);
-       }
-
-       pReq->xdi_dma_descriptor_operation.Req = 0;
-       pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
-
-       pReq->xdi_dma_descriptor_operation.info.operation =     IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_number  = -1;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
-
-       (*request)((ENTITY *)pReq);
-
-       if (!pReq->xdi_dma_descriptor_operation.info.operation &&
-           (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
-           pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
-               *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
-               return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
-       } else {
-               return (-1);
-       }
-}
-
-static void diva_free_dma_descriptor(IDI_CALL request, int nr) {
-       ENTITY e;
-       IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;
-
-       if (!request || (nr < 0)) {
-               return;
-       }
-
-       pReq->xdi_dma_descriptor_operation.Req = 0;
-       pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
-
-       pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_number  = nr;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
-
-       (*request)((ENTITY *)pReq);
-}
diff --git a/drivers/isdn/hardware/eicon/debug_if.h b/drivers/isdn/hardware/eicon/debug_if.h
deleted file mode 100644 (file)
index fc5953a..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *
- Copyright (c) Eicon Technology Corporation, 2000.
- *
- This source file is supplied for the use with Eicon
- Technology Corporation's range of DIVA Server Adapters.
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_DEBUG_IF_H__
-#define __DIVA_DEBUG_IF_H__
-#define MSG_TYPE_DRV_ID                0x0001
-#define MSG_TYPE_FLAGS         0x0002
-#define MSG_TYPE_STRING                0x0003
-#define MSG_TYPE_BINARY                0x0004
-#define MSG_TYPE_MLOG     0x0005
-
-#define MSG_FRAME_MAX_SIZE 2150
-
-typedef struct _diva_dbg_entry_head {
-       dword sequence;
-       dword time_sec;
-       dword time_usec;
-       dword facility;
-       dword dli;
-       dword drv_id;
-       dword di_cpu;
-       dword data_length;
-} diva_dbg_entry_head_t;
-
-int diva_maint_init(byte *base, unsigned long length, int do_init);
-void *diva_maint_finit(void);
-dword diva_dbg_q_length(void);
-diva_dbg_entry_head_t *diva_maint_get_message(word *size,
-                                             diva_os_spin_lock_magic_t *old_irql);
-void diva_maint_ack_message(int do_release,
-                           diva_os_spin_lock_magic_t *old_irql);
-void diva_maint_prtComp(char *format, ...);
-void diva_maint_wakeup_read(void);
-int diva_get_driver_info(dword id, byte *data, int data_length);
-int diva_get_driver_dbg_mask(dword id, byte *data);
-int diva_set_driver_dbg_mask(dword id, dword mask);
-void diva_mnt_remove_xdi_adapter(const DESCRIPTOR *d);
-void diva_mnt_add_xdi_adapter(const DESCRIPTOR *d);
-int diva_mnt_shutdown_xdi_adapters(void);
-
-#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127
-int diva_set_trace_filter(int filter_length, const char *filter);
-int diva_get_trace_filter(int max_length, char *filter);
-
-
-#define DITRACE_CMD_GET_DRIVER_INFO   1
-#define DITRACE_READ_DRIVER_DBG_MASK  2
-#define DITRACE_WRITE_DRIVER_DBG_MASK 3
-#define DITRACE_READ_TRACE_ENTRY      4
-#define DITRACE_READ_TRACE_ENTRYS     5
-#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6
-#define DITRACE_READ_SELECTIVE_TRACE_FILTER  7
-
-/*
-  Trace lavels for debug via management interface
-*/
-#define DIVA_MGT_DBG_TRACE          0x00000001 /* All trace messages from the card */
-#define DIVA_MGT_DBG_DCHAN          0x00000002 /* All D-channel relater trace messages */
-#define DIVA_MGT_DBG_MDM_PROGRESS   0x00000004 /* Modem progress events */
-#define DIVA_MGT_DBG_FAX_PROGRESS   0x00000008 /* Fax progress events */
-#define DIVA_MGT_DBG_IFC_STATISTICS 0x00000010 /* Interface call statistics */
-#define DIVA_MGT_DBG_MDM_STATISTICS 0x00000020 /* Global modem statistics   */
-#define DIVA_MGT_DBG_FAX_STATISTICS 0x00000040 /* Global call statistics    */
-#define DIVA_MGT_DBG_LINE_EVENTS    0x00000080 /* Line state events */
-#define DIVA_MGT_DBG_IFC_EVENTS     0x00000100 /* Interface/L1/L2 state events */
-#define DIVA_MGT_DBG_IFC_BCHANNEL   0x00000200 /* B-Channel trace for all channels */
-#define DIVA_MGT_DBG_IFC_AUDIO      0x00000400 /* Audio Tap trace for all channels */
-
-# endif /* DEBUG_IF___H */
diff --git a/drivers/isdn/hardware/eicon/debuglib.c b/drivers/isdn/hardware/eicon/debuglib.c
deleted file mode 100644 (file)
index d5b1092..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "debuglib.h"
-
-#ifdef DIVA_NO_DEBUGLIB
-static DIVA_DI_PRINTF dprintf;
-#else /* DIVA_NO_DEBUGLIB */
-
-_DbgHandle_ myDriverDebugHandle = { 0 /*!Registered*/, DBG_HANDLE_VERSION };
-DIVA_DI_PRINTF dprintf = no_printf;
-/*****************************************************************************/
-#define DBG_FUNC(name)                                                 \
-       void                                                            \
-       myDbgPrint_##name(char *format, ...)                            \
-       { va_list ap;                                                   \
-               if (myDriverDebugHandle.dbg_prt)                        \
-               { va_start(ap, format);                         \
-                       (myDriverDebugHandle.dbg_prt)                   \
-                               (myDriverDebugHandle.id, DLI_##name, format, ap); \
-                       va_end(ap);                                     \
-               } }
-DBG_FUNC(LOG)
-DBG_FUNC(FTL)
-DBG_FUNC(ERR)
-DBG_FUNC(TRC)
-DBG_FUNC(MXLOG)
-DBG_FUNC(FTL_MXLOG)
-void
-myDbgPrint_EVL(long msgID, ...)
-{ va_list ap;
-       if (myDriverDebugHandle.dbg_ev)
-       { va_start(ap, msgID);
-               (myDriverDebugHandle.dbg_ev)
-                       (myDriverDebugHandle.id, (unsigned long)msgID, ap);
-               va_end(ap);
-       } }
-DBG_FUNC(REG)
-DBG_FUNC(MEM)
-DBG_FUNC(SPL)
-DBG_FUNC(IRP)
-DBG_FUNC(TIM)
-DBG_FUNC(BLK)
-DBG_FUNC(TAPI)
-DBG_FUNC(NDIS)
-DBG_FUNC(CONN)
-DBG_FUNC(STAT)
-DBG_FUNC(SEND)
-DBG_FUNC(RECV)
-DBG_FUNC(PRV0)
-DBG_FUNC(PRV1)
-DBG_FUNC(PRV2)
-DBG_FUNC(PRV3)
-/*****************************************************************************/
-int
-DbgRegister(char *drvName, char *drvTag, unsigned long dbgMask)
-{
-       int len;
-/*
- * deregister (if already registered) and zero out myDriverDebugHandle
- */
-       DbgDeregister();
-/*
- * initialize the debug handle
- */
-       myDriverDebugHandle.Version = DBG_HANDLE_VERSION;
-       myDriverDebugHandle.id  = -1;
-       myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG);
-       len = strlen(drvName);
-       memcpy(myDriverDebugHandle.drvName, drvName,
-              (len < sizeof(myDriverDebugHandle.drvName)) ?
-              len : sizeof(myDriverDebugHandle.drvName) - 1);
-       len = strlen(drvTag);
-       memcpy(myDriverDebugHandle.drvTag, drvTag,
-              (len < sizeof(myDriverDebugHandle.drvTag)) ?
-              len : sizeof(myDriverDebugHandle.drvTag) - 1);
-/*
- * Try to register debugging via old (and only) interface
- */
-       dprintf("\000\377", &myDriverDebugHandle);
-       if (myDriverDebugHandle.dbg_prt)
-       {
-               return (1);
-       }
-/*
- * Check if we registered with an old maint driver (see debuglib.h)
- */
-       if (myDriverDebugHandle.dbg_end != NULL
-            /* location of 'dbg_prt' in _OldDbgHandle_ struct */
-            && (myDriverDebugHandle.regTime.LowPart ||
-                myDriverDebugHandle.regTime.HighPart))
-               /* same location as in _OldDbgHandle_ struct */
-       {
-               dprintf("%s: Cannot log to old maint driver !", drvName);
-               myDriverDebugHandle.dbg_end =
-                       ((_OldDbgHandle_ *)&myDriverDebugHandle)->dbg_end;
-               DbgDeregister();
-       }
-       return (0);
-}
-/*****************************************************************************/
-void
-DbgSetLevel(unsigned long dbgMask)
-{
-       myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG);
-}
-/*****************************************************************************/
-void
-DbgDeregister(void)
-{
-       if (myDriverDebugHandle.dbg_end)
-       {
-               (myDriverDebugHandle.dbg_end)(&myDriverDebugHandle);
-       }
-       memset(&myDriverDebugHandle, 0, sizeof(myDriverDebugHandle));
-}
-void xdi_dbg_xlog(char *x, ...) {
-       va_list ap;
-       va_start(ap, x);
-       if (myDriverDebugHandle.dbg_end &&
-           (myDriverDebugHandle.dbg_irq || myDriverDebugHandle.dbg_old) &&
-           (myDriverDebugHandle.dbgMask & DL_STAT)) {
-               if (myDriverDebugHandle.dbg_irq) {
-                       (*(myDriverDebugHandle.dbg_irq))(myDriverDebugHandle.id,
-                                                        (x[0] != 0) ? DLI_TRC : DLI_XLOG, x, ap);
-               } else {
-                       (*(myDriverDebugHandle.dbg_old))(myDriverDebugHandle.id, x, ap);
-               }
-       }
-       va_end(ap);
-}
-/*****************************************************************************/
-#endif /* DIVA_NO_DEBUGLIB */
diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h
deleted file mode 100644 (file)
index 6dcbf6a..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#if !defined(__DEBUGLIB_H__)
-#define __DEBUGLIB_H__
-#include <stdarg.h>
-/*
- * define global debug priorities
- */
-#define DL_LOG  0x00000001 /* always worth mentioning */
-#define DL_FTL  0x00000002 /* always sampled error    */
-#define DL_ERR  0x00000004 /* any kind of error       */
-#define DL_TRC  0x00000008 /* verbose information     */
-#define DL_XLOG  0x00000010 /* old xlog info           */
-#define DL_MXLOG 0x00000020 /* maestra xlog info    */
-#define DL_FTL_MXLOG 0x00000021 /* fatal maestra xlog info */
-#define DL_EVL  0x00000080 /* special NT eventlog msg */
-#define DL_COMPAT (DL_MXLOG | DL_XLOG)
-#define DL_PRIOR_MASK (DL_EVL | DL_COMPAT | DL_TRC | DL_ERR | DL_FTL | DL_LOG)
-#define DLI_LOG  0x0100
-#define DLI_FTL  0x0200
-#define DLI_ERR  0x0300
-#define DLI_TRC  0x0400
-#define DLI_XLOG 0x0500
-#define DLI_MXLOG 0x0600
-#define DLI_FTL_MXLOG 0x0600
-#define DLI_EVL  0x0800
-/*
- * define OS (operating system interface) debuglevel
- */
-#define DL_REG  0x00000100 /* init/query registry     */
-#define DL_MEM  0x00000200 /* memory management       */
-#define DL_SPL  0x00000400 /* event/spinlock handling */
-#define DL_IRP  0x00000800 /* I/O request handling    */
-#define DL_TIM  0x00001000 /* timer/watchdog handling */
-#define DL_BLK  0x00002000 /* raw data block contents */
-#define DL_OS_MASK (DL_BLK | DL_TIM | DL_IRP | DL_SPL | DL_MEM | DL_REG)
-#define DLI_REG  0x0900
-#define DLI_MEM  0x0A00
-#define DLI_SPL  0x0B00
-#define DLI_IRP  0x0C00
-#define DLI_TIM  0x0D00
-#define DLI_BLK  0x0E00
-/*
- * define ISDN (connection interface) debuglevel
- */
-#define DL_TAPI  0x00010000 /* debug TAPI interface    */
-#define DL_NDIS  0x00020000 /* debug NDIS interface    */
-#define DL_CONN  0x00040000 /* connection handling     */
-#define DL_STAT  0x00080000 /* trace state machines    */
-#define DL_SEND  0x00100000 /* trace raw xmitted data  */
-#define DL_RECV  0x00200000 /* trace raw received data */
-#define DL_DATA  (DL_SEND | DL_RECV)
-#define DL_ISDN_MASK (DL_DATA | DL_STAT | DL_CONN | DL_NDIS | DL_TAPI)
-#define DLI_TAPI 0x1100
-#define DLI_NDIS 0x1200
-#define DLI_CONN 0x1300
-#define DLI_STAT 0x1400
-#define DLI_SEND 0x1500
-#define DLI_RECV 0x1600
-/*
- * define some private (unspecified) debuglevel
- */
-#define DL_PRV0  0x01000000
-#define DL_PRV1  0x02000000
-#define DL_PRV2  0x04000000
-#define DL_PRV3  0x08000000
-#define DL_PRIV_MASK (DL_PRV0 | DL_PRV1 | DL_PRV2 | DL_PRV3)
-#define DLI_PRV0 0x1900
-#define DLI_PRV1 0x1A00
-#define DLI_PRV2 0x1B00
-#define DLI_PRV3 0x1C00
-#define DT_INDEX(x)  ((x) & 0x000F)
-#define DL_INDEX(x)  ((((x) >> 8) & 0x00FF) - 1)
-#define DLI_NAME(x)  ((x) & 0xFF00)
-/*
- * Debug mask for kernel mode tracing, if set the output is also sent to
- * the system debug function. Requires that the project is compiled
- * with _KERNEL_DBG_PRINT_
- */
-#define DL_TO_KERNEL    0x40000000
-
-#ifdef DIVA_NO_DEBUGLIB
-#define myDbgPrint_LOG(x...) do { } while (0);
-#define myDbgPrint_FTL(x...) do { } while (0);
-#define myDbgPrint_ERR(x...) do { } while (0);
-#define myDbgPrint_TRC(x...) do { } while (0);
-#define myDbgPrint_MXLOG(x...) do { } while (0);
-#define myDbgPrint_EVL(x...) do { } while (0);
-#define myDbgPrint_REG(x...) do { } while (0);
-#define myDbgPrint_MEM(x...) do { } while (0);
-#define myDbgPrint_SPL(x...) do { } while (0);
-#define myDbgPrint_IRP(x...) do { } while (0);
-#define myDbgPrint_TIM(x...) do { } while (0);
-#define myDbgPrint_BLK(x...) do { } while (0);
-#define myDbgPrint_TAPI(x...) do { } while (0);
-#define myDbgPrint_NDIS(x...) do { } while (0);
-#define myDbgPrint_CONN(x...) do { } while (0);
-#define myDbgPrint_STAT(x...) do { } while (0);
-#define myDbgPrint_SEND(x...) do { } while (0);
-#define myDbgPrint_RECV(x...) do { } while (0);
-#define myDbgPrint_PRV0(x...) do { } while (0);
-#define myDbgPrint_PRV1(x...) do { } while (0);
-#define myDbgPrint_PRV2(x...) do { } while (0);
-#define myDbgPrint_PRV3(x...) do { } while (0);
-#define DBG_TEST(func, args) do { } while (0);
-#define DBG_EVL_ID(args) do { } while (0);
-
-#else /* DIVA_NO_DEBUGLIB */
-/*
- * define low level macros for formatted & raw debugging
- */
-#define DBG_DECL(func) extern void  myDbgPrint_##func(char *, ...);
-DBG_DECL(LOG)
-DBG_DECL(FTL)
-DBG_DECL(ERR)
-DBG_DECL(TRC)
-DBG_DECL(MXLOG)
-DBG_DECL(FTL_MXLOG)
-extern void  myDbgPrint_EVL(long, ...);
-DBG_DECL(REG)
-DBG_DECL(MEM)
-DBG_DECL(SPL)
-DBG_DECL(IRP)
-DBG_DECL(TIM)
-DBG_DECL(BLK)
-DBG_DECL(TAPI)
-DBG_DECL(NDIS)
-DBG_DECL(CONN)
-DBG_DECL(STAT)
-DBG_DECL(SEND)
-DBG_DECL(RECV)
-DBG_DECL(PRV0)
-DBG_DECL(PRV1)
-DBG_DECL(PRV2)
-DBG_DECL(PRV3)
-#ifdef _KERNEL_DBG_PRINT_
-/*
- * tracing to maint and kernel if selected in the trace mask.
- */
-#define DBG_TEST(func, args)                                           \
-       { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func) \
-               {                                                       \
-                       if ((myDriverDebugHandle.dbgMask) & DL_TO_KERNEL) \
-                       { DbgPrint args; DbgPrint("\r\n"); }            \
-                       myDbgPrint_##func args;                 \
-               } }
-#else
-/*
- * Standard tracing to maint driver.
- */
-#define DBG_TEST(func, args)                                           \
-       { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func) \
-               { myDbgPrint_##func args;                               \
-               } }
-#endif
-/*
- * For event level debug use a separate define, the parameter are
- * different and cause compiler errors on some systems.
- */
-#define DBG_EVL_ID(args)                                               \
-       { if ((myDriverDebugHandle.dbgMask) & (unsigned long)DL_EVL)    \
-               { myDbgPrint_EVL args;                                  \
-               } }
-
-#endif /* DIVA_NO_DEBUGLIB */
-
-#define DBG_LOG(args)  DBG_TEST(LOG, args)
-#define DBG_FTL(args)  DBG_TEST(FTL, args)
-#define DBG_ERR(args)  DBG_TEST(ERR, args)
-#define DBG_TRC(args)  DBG_TEST(TRC, args)
-#define DBG_MXLOG(args)  DBG_TEST(MXLOG, args)
-#define DBG_FTL_MXLOG(args) DBG_TEST(FTL_MXLOG, args)
-#define DBG_EVL(args)  DBG_EVL_ID(args)
-#define DBG_REG(args)  DBG_TEST(REG, args)
-#define DBG_MEM(args)  DBG_TEST(MEM, args)
-#define DBG_SPL(args)  DBG_TEST(SPL, args)
-#define DBG_IRP(args)  DBG_TEST(IRP, args)
-#define DBG_TIM(args)  DBG_TEST(TIM, args)
-#define DBG_BLK(args)  DBG_TEST(BLK, args)
-#define DBG_TAPI(args)  DBG_TEST(TAPI, args)
-#define DBG_NDIS(args)  DBG_TEST(NDIS, args)
-#define DBG_CONN(args)  DBG_TEST(CONN, args)
-#define DBG_STAT(args)  DBG_TEST(STAT, args)
-#define DBG_SEND(args)  DBG_TEST(SEND, args)
-#define DBG_RECV(args)  DBG_TEST(RECV, args)
-#define DBG_PRV0(args)  DBG_TEST(PRV0, args)
-#define DBG_PRV1(args)  DBG_TEST(PRV1, args)
-#define DBG_PRV2(args)  DBG_TEST(PRV2, args)
-#define DBG_PRV3(args)  DBG_TEST(PRV3, args)
-/*
- * prototypes for debug register/deregister functions in "debuglib.c"
- */
-#ifdef DIVA_NO_DEBUGLIB
-#define DbgRegister(name, tag, mask) do { } while (0)
-#define DbgDeregister() do { } while (0)
-#define DbgSetLevel(mask) do { } while (0)
-#else
-extern DIVA_DI_PRINTF dprintf;
-extern int  DbgRegister(char *drvName, char *drvTag, unsigned long dbgMask);
-extern void DbgDeregister(void);
-extern void DbgSetLevel(unsigned long dbgMask);
-#endif
-/*
- * driver internal structure for debug handling;
- * in client drivers this structure is maintained in "debuglib.c",
- * in the debug driver "debug.c" maintains a chain of such structs.
- */
-typedef struct _DbgHandle_ *pDbgHandle;
-typedef void (*DbgEnd)(pDbgHandle);
-typedef void (*DbgLog)(unsigned short, int, char *, va_list);
-typedef void (*DbgOld)(unsigned short, char *, va_list);
-typedef void (*DbgEv)(unsigned short, unsigned long, va_list);
-typedef void (*DbgIrq)(unsigned short, int, char *, va_list);
-typedef struct _DbgHandle_
-{ char    Registered; /* driver successfully registered */
-#define DBG_HANDLE_REG_NEW 0x01  /* this (new) structure    */
-#define DBG_HANDLE_REG_OLD 0x7f  /* old structure (see below)  */
-       char    Version;  /* version of this structure  */
-#define DBG_HANDLE_VERSION 1   /* contains dbg_old function now */
-#define DBG_HANDLE_VER_EXT  2           /* pReserved points to extended info*/
-       short               id;   /* internal id of registered driver */
-       struct _DbgHandle_ *next;   /* ptr to next registered driver    */
-       struct /*LARGE_INTEGER*/ {
-               unsigned long LowPart;
-               long          HighPart;
-       }     regTime;  /* timestamp for registration       */
-       void               *pIrp;   /* ptr to pending i/o request       */
-       unsigned long       dbgMask;  /* current debug mask               */
-       char                drvName[128]; /* ASCII name of registered driver  */
-       char                drvTag[64]; /* revision string     */
-       DbgEnd              dbg_end;  /* function for debug closing       */
-       DbgLog              dbg_prt;  /* function for debug appending     */
-       DbgOld              dbg_old;  /* function for old debug appending */
-       DbgEv       dbg_ev;  /* function for Windows NT Eventlog */
-       DbgIrq    dbg_irq;  /* function for irql checked debug  */
-       void      *pReserved3;
-} _DbgHandle_;
-extern _DbgHandle_ myDriverDebugHandle;
-typedef struct _OldDbgHandle_
-{ struct _OldDbgHandle_ *next;
-       void                *pIrp;
-       long    regTime[2];
-       unsigned long       dbgMask;
-       short               id;
-       char                drvName[78];
-       DbgEnd              dbg_end;
-       DbgLog              dbg_prt;
-} _OldDbgHandle_;
-/* the differences in DbgHandles
-   old:    tmp:     new:
-   0 long next  char Registered  char Registered
-   char filler   char Version
-   short id    short id
-   4 long pIrp  long    regTime.lo  long next
-   8 long    regTime.lo long    regTime.hi  long    regTime.lo
-   12 long    regTime.hi long next   long regTime.hi
-   16 long dbgMask  long pIrp   long pIrp
-   20 short id   long dbgMask   long dbgMask
-   22 char    drvName[78] ..
-   24 ..     char drvName[16]  char drvName[16]
-   40 ..     char drvTag[64]  char drvTag[64]
-   100 void *dbg_end ..      ..
-   104 void *dbg_prt void *dbg_end  void *dbg_end
-   108 ..     void *dbg_prt  void *dbg_prt
-   112 ..     ..      void *dbg_old
-   116 ..     ..      void *dbg_ev
-   120 ..     ..      void *dbg_irq
-   124 ..     ..      void *pReserved3
-   ( new->id == 0 && *((short *)&new->dbgMask) == -1 ) identifies "old",
-   new->Registered and new->Version overlay old->next,
-   new->next overlays old->pIrp, new->regTime matches old->regTime and
-   thus these fields can be maintained in new struct whithout trouble;
-   id, dbgMask, drvName, dbg_end and dbg_prt need special handling !
-*/
-#define DBG_EXT_TYPE_CARD_TRACE     0x00000001
-typedef struct
-{
-       unsigned long ExtendedType;
-       union
-       {
-               /* DBG_EXT_TYPE_CARD_TRACE */
-               struct
-               {
-                       void (*MaskChangedNotify)(void *pContext);
-                       unsigned long ModuleTxtMask;
-                       unsigned long DebugLevel;
-                       unsigned long B_ChannelMask;
-                       unsigned long LogBufferSize;
-               } CardTrace;
-       } Data;
-} _DbgExtendedInfo_;
-#ifndef DIVA_NO_DEBUGLIB
-/* -------------------------------------------------------------
-   Function used for xlog-style debug
-   ------------------------------------------------------------- */
-#define XDI_USE_XLOG 1
-void xdi_dbg_xlog(char *x, ...);
-#endif /* DIVA_NO_DEBUGLIB */
-#endif /* __DEBUGLIB_H__ */
diff --git a/drivers/isdn/hardware/eicon/dfifo.h b/drivers/isdn/hardware/eicon/dfifo.h
deleted file mode 100644 (file)
index 6a1d333..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_IDI_DFIFO_INC__
-#define __DIVA_IDI_DFIFO_INC__
-#define DIVA_DFIFO_CACHE_SZ   64 /* Used to isolate pipe from
-                                   rest of the world
-                                   should be divisible by 4
-                                */
-#define DIVA_DFIFO_RAW_SZ    (2512 * 8)
-#define DIVA_DFIFO_DATA_SZ   68
-#define DIVA_DFIFO_HDR_SZ    4
-#define DIVA_DFIFO_SEGMENT_SZ  (DIVA_DFIFO_DATA_SZ + DIVA_DFIFO_HDR_SZ)
-#define DIVA_DFIFO_SEGMENTS   ((DIVA_DFIFO_RAW_SZ) / (DIVA_DFIFO_SEGMENT_SZ) + 1)
-#define DIVA_DFIFO_MEM_SZ (                                            \
-               (DIVA_DFIFO_SEGMENT_SZ) * (DIVA_DFIFO_SEGMENTS) +       \
-               (DIVA_DFIFO_CACHE_SZ) * 2                               \
-               )
-#define DIVA_DFIFO_STEP DIVA_DFIFO_SEGMENT_SZ
-/* -------------------------------------------------------------------------
-   Block header layout is:
-   byte[0] -> flags
-   byte[1] -> length of data in block
-   byte[2] -> reserved
-   byte[4] -> reserved
-   ------------------------------------------------------------------------- */
-#define DIVA_DFIFO_WRAP   0x80 /* This is the last block in fifo   */
-#define DIVA_DFIFO_READY  0x40 /* This block is ready for processing */
-#define DIVA_DFIFO_LAST   0x20 /* This block is last in message      */
-#define DIVA_DFIFO_AUTO   0x10 /* Don't look for 'ready', don't ack */
-int diva_dfifo_create(void *start, int length);
-#endif
diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c
deleted file mode 100644 (file)
index cd3fba1..0000000
+++ /dev/null
@@ -1,835 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di_defs.h"
-#include "di.h"
-#if !defined USE_EXTENDED_DEBUGS
-#include "dimaint.h"
-#else
-#define dprintf
-#endif
-#include "io.h"
-#include "dfifo.h"
-#define PR_RAM  ((struct pr_ram *)0)
-#define RAM ((struct dual *)0)
-/*------------------------------------------------------------------*/
-/* local function prototypes                                        */
-/*------------------------------------------------------------------*/
-void pr_out(ADAPTER *a);
-byte pr_dpc(ADAPTER *a);
-static byte pr_ready(ADAPTER *a);
-static byte isdn_rc(ADAPTER *, byte, byte, byte, word, dword, dword);
-static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word);
-/* -----------------------------------------------------------------
-   Functions used for the extended XDI Debug
-   macros
-   global convergence counter (used by all adapters)
-   Look by the implementation part of the functions
-   about the parameters.
-   If you change the dubugging parameters, then you should update
-   the aididbg.doc in the IDI doc's.
-   ----------------------------------------------------------------- */
-#if defined(XDI_USE_XLOG)
-#define XDI_A_NR(_x_) ((byte)(((ISDN_ADAPTER *)(_x_->io))->ANum))
-static void xdi_xlog(byte *msg, word code, int length);
-static byte xdi_xlog_sec = 0;
-#else
-#define XDI_A_NR(_x_) ((byte)0)
-#endif
-static void xdi_xlog_rc_event(byte Adapter,
-                             byte Id, byte Ch, byte Rc, byte cb, byte type);
-static void xdi_xlog_request(byte Adapter, byte Id,
-                            byte Ch, byte Req, byte type);
-static void xdi_xlog_ind(byte Adapter,
-                        byte Id,
-                        byte Ch,
-                        byte Ind,
-                        byte rnr_valid,
-                        byte rnr,
-                        byte type);
-/*------------------------------------------------------------------*/
-/* output function                                                  */
-/*------------------------------------------------------------------*/
-void pr_out(ADAPTER *a)
-{
-       byte e_no;
-       ENTITY *this = NULL;
-       BUFFERS *X;
-       word length;
-       word i;
-       word clength;
-       REQ *ReqOut;
-       byte more;
-       byte ReadyCount;
-       byte ReqCount;
-       byte Id;
-       dtrc(dprintf("pr_out"));
-       /* while a request is pending ...                           */
-       e_no = look_req(a);
-       if (!e_no)
-       {
-               dtrc(dprintf("no_req"));
-               return;
-       }
-       ReadyCount = pr_ready(a);
-       if (!ReadyCount)
-       {
-               dtrc(dprintf("not_ready"));
-               return;
-       }
-       ReqCount = 0;
-       while (e_no && ReadyCount) {
-               next_req(a);
-               this = entity_ptr(a, e_no);
-#ifdef USE_EXTENDED_DEBUGS
-               if (!this)
-               {
-                       DBG_FTL(("XDI: [%02x] !A%d ==> NULL entity ptr - try to ignore",
-                                xdi_xlog_sec++, (int)((ISDN_ADAPTER *)a->io)->ANum))
-                               e_no = look_req(a);
-                       ReadyCount--;
-                       continue;
-               }
-               {
-                       DBG_TRC((">A%d Id=0x%x Req=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, this->Id, this->Req))
-                               }
-#else
-               dbug(dprintf("out:Req=%x,Id=%x,Ch=%x", this->Req, this->Id, this->ReqCh));
-#endif
-               /* get address of next available request buffer             */
-               ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)];
-#if defined(DIVA_ISTREAM)
-               if (!(a->tx_stream[this->Id]   &&
-                     this->Req == N_DATA)) {
-#endif
-                       /* now copy the data from the current data buffer into the  */
-                       /* adapters request buffer                                  */
-                       length = 0;
-                       i = this->XCurrent;
-                       X = PTR_X(a, this);
-                       while (i < this->XNum && length < 270) {
-                               clength = min((word)(270 - length), (word)(X[i].PLength-this->XOffset));
-                               a->ram_out_buffer(a,
-                                                 &ReqOut->XBuffer.P[length],
-                                                 PTR_P(a, this, &X[i].P[this->XOffset]),
-                                                 clength);
-                               length += clength;
-                               this->XOffset += clength;
-                               if (this->XOffset == X[i].PLength) {
-                                       this->XCurrent = (byte)++i;
-                                       this->XOffset = 0;
-                               }
-                       }
-#if defined(DIVA_ISTREAM)
-               } else { /* Use CMA extension in order to transfer data to the card */
-                       i = this->XCurrent;
-                       X = PTR_X(a, this);
-                       while (i < this->XNum) {
-                               diva_istream_write(a,
-                                                  this->Id,
-                                                  PTR_P(a, this, &X[i].P[0]),
-                                                  X[i].PLength,
-                                                  ((i + 1) == this->XNum),
-                                                  0, 0);
-                               this->XCurrent = (byte)++i;
-                       }
-                       length = 0;
-               }
-#endif
-               a->ram_outw(a, &ReqOut->XBuffer.length, length);
-               a->ram_out(a, &ReqOut->ReqId, this->Id);
-               a->ram_out(a, &ReqOut->ReqCh, this->ReqCh);
-               /* if it's a specific request (no ASSIGN) ...                */
-               if (this->Id & 0x1f) {
-                       /* if buffers are left in the list of data buffers do       */
-                       /* do chaining (LL_MDATA, N_MDATA)                          */
-                       this->More++;
-                       if (i < this->XNum && this->MInd) {
-                               xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->MInd,
-                                                a->IdTypeTable[this->No]);
-                               a->ram_out(a, &ReqOut->Req, this->MInd);
-                               more = true;
-                       }
-                       else {
-                               xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->Req,
-                                                a->IdTypeTable[this->No]);
-                               this->More |= XMOREF;
-                               a->ram_out(a, &ReqOut->Req, this->Req);
-                               more = false;
-                               if (a->FlowControlIdTable[this->ReqCh] == this->Id)
-                                       a->FlowControlSkipTable[this->ReqCh] = true;
-                               /*
-                                 Note that remove request was sent to the card
-                               */
-                               if (this->Req == REMOVE) {
-                                       a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_REMOVE_PENDING;
-                               }
-                       }
-                       /* if we did chaining, this entity is put back into the     */
-                       /* request queue                                            */
-                       if (more) {
-                               req_queue(a, this->No);
-                       }
-               }
-               /* else it's a ASSIGN                                       */
-               else {
-                       /* save the request code used for buffer chaining           */
-                       this->MInd = 0;
-                       if (this->Id == BLLC_ID) this->MInd = LL_MDATA;
-                       if (this->Id == NL_ID ||
-                           this->Id == TASK_ID ||
-                           this->Id == MAN_ID
-                               ) this->MInd = N_MDATA;
-                       /* send the ASSIGN                                          */
-                       a->IdTypeTable[this->No] = this->Id;
-                       xdi_xlog_request(XDI_A_NR(a), this->Id, this->ReqCh, this->Req, this->Id);
-                       this->More |= XMOREF;
-                       a->ram_out(a, &ReqOut->Req, this->Req);
-                       /* save the reference of the ASSIGN                         */
-                       assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference));
-               }
-               a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next));
-               ReadyCount--;
-               ReqCount++;
-               e_no = look_req(a);
-       }
-       /* send the filled request buffers to the ISDN adapter      */
-       a->ram_out(a, &PR_RAM->ReqInput,
-                  (byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount));
-       /* if it is a 'unreturncoded' UREMOVE request, remove the  */
-       /* Id from our table after sending the request             */
-       if (this && (this->Req == UREMOVE) && this->Id) {
-               Id = this->Id;
-               e_no = a->IdTable[Id];
-               free_entity(a, e_no);
-               for (i = 0; i < 256; i++)
-               {
-                       if (a->FlowControlIdTable[i] == Id)
-                               a->FlowControlIdTable[i] = 0;
-               }
-               a->IdTable[Id] = 0;
-               this->Id = 0;
-       }
-}
-static byte pr_ready(ADAPTER *a)
-{
-       byte ReadyCount;
-       ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) -
-                           a->ram_in(a, &PR_RAM->ReqInput));
-       if (!ReadyCount) {
-               if (!a->ReadyInt) {
-                       a->ram_inc(a, &PR_RAM->ReadyInt);
-                       a->ReadyInt++;
-               }
-       }
-       return ReadyCount;
-}
-/*------------------------------------------------------------------*/
-/* isdn interrupt handler                                           */
-/*------------------------------------------------------------------*/
-byte pr_dpc(ADAPTER *a)
-{
-       byte Count;
-       RC *RcIn;
-       IND *IndIn;
-       byte c;
-       byte RNRId;
-       byte Rc;
-       byte Ind;
-       /* if return codes are available ...                        */
-       if ((Count = a->ram_in(a, &PR_RAM->RcOutput)) != 0) {
-               dtrc(dprintf("#Rc=%x", Count));
-               /* get the buffer address of the first return code          */
-               RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)];
-               /* for all return codes do ...                              */
-               while (Count--) {
-                       if ((Rc = a->ram_in(a, &RcIn->Rc)) != 0) {
-                               dword tmp[2];
-                               /*
-                                 Get extended information, associated with return code
-                               */
-                               a->ram_in_buffer(a,
-                                                &RcIn->Reserved2[0],
-                                                (byte *)&tmp[0],
-                                                8);
-                               /* call return code handler, if it is not our return code   */
-                               /* the handler returns 2                                    */
-                               /* for all return codes we process, we clear the Rc field   */
-                               isdn_rc(a,
-                                       Rc,
-                                       a->ram_in(a, &RcIn->RcId),
-                                       a->ram_in(a, &RcIn->RcCh),
-                                       a->ram_inw(a, &RcIn->Reference),
-                                       tmp[0],  /* type of extended information */
-                                       tmp[1]); /* extended information        */
-                               a->ram_out(a, &RcIn->Rc, 0);
-                       }
-                       /* get buffer address of next return code                   */
-                       RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)];
-               }
-               /* clear all return codes (no chaining!)                    */
-               a->ram_out(a, &PR_RAM->RcOutput, 0);
-               /* call output function                                     */
-               pr_out(a);
-       }
-       /* clear RNR flag                                           */
-       RNRId = 0;
-       /* if indications are available ...                         */
-       if ((Count = a->ram_in(a, &PR_RAM->IndOutput)) != 0) {
-               dtrc(dprintf("#Ind=%x", Count));
-               /* get the buffer address of the first indication           */
-               IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)];
-               /* for all indications do ...                               */
-               while (Count--) {
-                       /* if the application marks an indication as RNR, all       */
-                       /* indications from the same Id delivered in this interrupt */
-                       /* are marked RNR                                           */
-                       if (RNRId && RNRId == a->ram_in(a, &IndIn->IndId)) {
-                               a->ram_out(a, &IndIn->Ind, 0);
-                               a->ram_out(a, &IndIn->RNR, true);
-                       }
-                       else {
-                               Ind = a->ram_in(a, &IndIn->Ind);
-                               if (Ind) {
-                                       RNRId = 0;
-                                       /* call indication handler, a return value of 2 means chain */
-                                       /* a return value of 1 means RNR                            */
-                                       /* for all indications we process, we clear the Ind field   */
-                                       c = isdn_ind(a,
-                                                    Ind,
-                                                    a->ram_in(a, &IndIn->IndId),
-                                                    a->ram_in(a, &IndIn->IndCh),
-                                                    &IndIn->RBuffer,
-                                                    a->ram_in(a, &IndIn->MInd),
-                                                    a->ram_inw(a, &IndIn->MLength));
-                                       if (c == 1) {
-                                               dtrc(dprintf("RNR"));
-                                               a->ram_out(a, &IndIn->Ind, 0);
-                                               RNRId = a->ram_in(a, &IndIn->IndId);
-                                               a->ram_out(a, &IndIn->RNR, true);
-                                       }
-                               }
-                       }
-                       /* get buffer address of next indication                    */
-                       IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)];
-               }
-               a->ram_out(a, &PR_RAM->IndOutput, 0);
-       }
-       return false;
-}
-byte scom_test_int(ADAPTER *a)
-{
-       return a->ram_in(a, (void *)0x3fe);
-}
-void scom_clear_int(ADAPTER *a)
-{
-       a->ram_out(a, (void *)0x3fe, 0);
-}
-/*------------------------------------------------------------------*/
-/* return code handler                                              */
-/*------------------------------------------------------------------*/
-static byte isdn_rc(ADAPTER *a,
-                   byte Rc,
-                   byte Id,
-                   byte Ch,
-                   word Ref,
-                   dword extended_info_type,
-                   dword extended_info)
-{
-       ENTITY *this;
-       byte e_no;
-       word i;
-       int cancel_rc;
-#ifdef USE_EXTENDED_DEBUGS
-       {
-               DBG_TRC(("<A%d Id=0x%x Rc=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Rc))
-                       }
-#else
-       dbug(dprintf("isdn_rc(Rc=%x,Id=%x,Ch=%x)", Rc, Id, Ch));
-#endif
-       /* check for ready interrupt                                */
-       if (Rc == READY_INT) {
-               xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 0, 0);
-               if (a->ReadyInt) {
-                       a->ReadyInt--;
-                       return 0;
-               }
-               return 2;
-       }
-       /* if we know this Id ...                                   */
-       e_no = a->IdTable[Id];
-       if (e_no) {
-               this = entity_ptr(a, e_no);
-               xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 0, a->IdTypeTable[this->No]);
-               this->RcCh = Ch;
-               /* if it is a return code to a REMOVE request, remove the   */
-               /* Id from our table                                        */
-               if ((a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_REMOVE_PENDING) &&
-                   (Rc == OK)) {
-                       if (a->IdTypeTable[e_no] == NL_ID) {
-                               if (a->RcExtensionSupported &&
-                                   (extended_info_type != DIVA_RC_TYPE_REMOVE_COMPLETE)) {
-                                       dtrc(dprintf("XDI: N-REMOVE, A(%02x) Id:%02x, ignore RC=OK",
-                                                    XDI_A_NR(a), Id));
-                                       return (0);
-                               }
-                               if (extended_info_type == DIVA_RC_TYPE_REMOVE_COMPLETE)
-                                       a->RcExtensionSupported = true;
-                       }
-                       a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_REMOVE_PENDING;
-                       a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_NO_RC_CANCELLING;
-                       free_entity(a, e_no);
-                       for (i = 0; i < 256; i++)
-                       {
-                               if (a->FlowControlIdTable[i] == Id)
-                                       a->FlowControlIdTable[i] = 0;
-                       }
-                       a->IdTable[Id] = 0;
-                       this->Id = 0;
-                       /* ---------------------------------------------------------------
-                          If we send N_DISC or N_DISK_ACK after we have received OK_FC
-                          then the card will respond with OK_FC and later with RC==OK.
-                          If we send N_REMOVE in this state we will receive only RC==OK
-                          This will create the state in that the XDI is waiting for the
-                          additional RC and does not delivery the RC to the client. This
-                          code corrects the counter of outstanding RC's in this case.
-                          --------------------------------------------------------------- */
-                       if ((this->More & XMOREC) > 1) {
-                               this->More &= ~XMOREC;
-                               this->More |= 1;
-                               dtrc(dprintf("XDI: correct MORE on REMOVE A(%02x) Id:%02x",
-                                            XDI_A_NR(a), Id));
-                       }
-               }
-               if (Rc == OK_FC) {
-                       a->FlowControlIdTable[Ch] = Id;
-                       a->FlowControlSkipTable[Ch] = false;
-                       this->Rc = Rc;
-                       this->More &= ~(XBUSY | XMOREC);
-                       this->complete = 0xff;
-                       xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
-                       CALLBACK(a, this);
-                       return 0;
-               }
-               /*
-                 New protocol code sends return codes that comes from release
-                 of flow control condition marked with DIVA_RC_TYPE_OK_FC extended
-                 information element type.
-                 If like return code arrives then application is able to process
-                 all return codes self and XDI should not cances return codes.
-                 This return code does not decrement XMOREC partial return code
-                 counter due to fact that it was no request for this return code,
-                 also XMOREC was not incremented.
-               */
-               if (extended_info_type == DIVA_RC_TYPE_OK_FC) {
-                       a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_NO_RC_CANCELLING;
-                       this->Rc = Rc;
-                       this->complete = 0xff;
-                       xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
-                       DBG_TRC(("XDI OK_FC A(%02x) Id:%02x Ch:%02x Rc:%02x",
-                                XDI_A_NR(a), Id, Ch, Rc))
-                               CALLBACK(a, this);
-                       return 0;
-               }
-               cancel_rc = !(a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_NO_RC_CANCELLING);
-               if (cancel_rc && (a->FlowControlIdTable[Ch] == Id))
-               {
-                       a->FlowControlIdTable[Ch] = 0;
-                       if ((Rc != OK) || !a->FlowControlSkipTable[Ch])
-                       {
-                               this->Rc = Rc;
-                               if (Ch == this->ReqCh)
-                               {
-                                       this->More &= ~(XBUSY | XMOREC);
-                                       this->complete = 0xff;
-                               }
-                               xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
-                               CALLBACK(a, this);
-                       }
-                       return 0;
-               }
-               if (this->More & XMOREC)
-                       this->More--;
-               /* call the application callback function                   */
-               if (((!cancel_rc) || (this->More & XMOREF)) && !(this->More & XMOREC)) {
-                       this->Rc = Rc;
-                       this->More &= ~XBUSY;
-                       this->complete = 0xff;
-                       xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
-                       CALLBACK(a, this);
-               }
-               return 0;
-       }
-       /* if it's an ASSIGN return code check if it's a return     */
-       /* code to an ASSIGN request from us                        */
-       if ((Rc & 0xf0) == ASSIGN_RC) {
-               e_no = get_assign(a, Ref);
-               if (e_no) {
-                       this = entity_ptr(a, e_no);
-                       this->Id = Id;
-                       xdi_xlog_rc_event(XDI_A_NR(a), Id, Ch, Rc, 2, a->IdTypeTable[this->No]);
-                       /* call the application callback function                   */
-                       this->Rc = Rc;
-                       this->More &= ~XBUSY;
-                       this->complete = 0xff;
-#if defined(DIVA_ISTREAM) /* { */
-                       if ((Rc == ASSIGN_OK) && a->ram_offset &&
-                           (a->IdTypeTable[this->No] == NL_ID) &&
-                           ((extended_info_type == DIVA_RC_TYPE_RX_DMA) ||
-                            (extended_info_type == DIVA_RC_TYPE_CMA_PTR)) &&
-                           extended_info) {
-                               dword offset = (*(a->ram_offset)) (a);
-                               dword tmp[2];
-                               extended_info -= offset;
-#ifdef PLATFORM_GT_32BIT
-                               a->ram_in_dw(a, (void *)ULongToPtr(extended_info), (dword *)&tmp[0], 2);
-#else
-                               a->ram_in_dw(a, (void *)extended_info, (dword *)&tmp[0], 2);
-#endif
-                               a->tx_stream[Id]  = tmp[0];
-                               a->rx_stream[Id]  = tmp[1];
-                               if (extended_info_type == DIVA_RC_TYPE_RX_DMA) {
-                                       DBG_TRC(("Id=0x%x RxDMA=%08x:%08x",
-                                                Id, a->tx_stream[Id], a->rx_stream[Id]))
-                                               a->misc_flags_table[this->No] |= DIVA_MISC_FLAGS_RX_DMA;
-                               } else {
-                                       DBG_TRC(("Id=0x%x CMA=%08x:%08x",
-                                                Id, a->tx_stream[Id], a->rx_stream[Id]))
-                                               a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
-                                       a->rx_pos[Id]     = 0;
-                                       a->rx_stream[Id] -= offset;
-                               }
-                               a->tx_pos[Id]     = 0;
-                               a->tx_stream[Id] -= offset;
-                       } else {
-                               a->tx_stream[Id] = 0;
-                               a->rx_stream[Id] = 0;
-                               a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
-                       }
-#endif /* } */
-                       CALLBACK(a, this);
-                       if (Rc == ASSIGN_OK) {
-                               a->IdTable[Id] = e_no;
-                       }
-                       else
-                       {
-                               free_entity(a, e_no);
-                               for (i = 0; i < 256; i++)
-                               {
-                                       if (a->FlowControlIdTable[i] == Id)
-                                               a->FlowControlIdTable[i] = 0;
-                               }
-                               a->IdTable[Id] = 0;
-                               this->Id = 0;
-                       }
-                       return 1;
-               }
-       }
-       return 2;
-}
-/*------------------------------------------------------------------*/
-/* indication handler                                               */
-/*------------------------------------------------------------------*/
-static byte isdn_ind(ADAPTER *a,
-                    byte Ind,
-                    byte Id,
-                    byte Ch,
-                    PBUFFER *RBuffer,
-                    byte MInd,
-                    word MLength)
-{
-       ENTITY *this;
-       word clength;
-       word offset;
-       BUFFERS *R;
-       byte *cma = NULL;
-#ifdef USE_EXTENDED_DEBUGS
-       {
-               DBG_TRC(("<A%d Id=0x%x Ind=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Ind))
-                       }
-#else
-       dbug(dprintf("isdn_ind(Ind=%x,Id=%x,Ch=%x)", Ind, Id, Ch));
-#endif
-       if (a->IdTable[Id]) {
-               this = entity_ptr(a, a->IdTable[Id]);
-               this->IndCh = Ch;
-               xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind,
-                            0/* rnr_valid */, 0 /* rnr */, a->IdTypeTable[this->No]);
-               /* if the Receive More flag is not yet set, this is the     */
-               /* first buffer of the packet                               */
-               if (this->RCurrent == 0xff) {
-                       /* check for receive buffer chaining                        */
-                       if (Ind == this->MInd) {
-                               this->complete = 0;
-                               this->Ind = MInd;
-                       }
-                       else {
-                               this->complete = 1;
-                               this->Ind = Ind;
-                       }
-                       /* call the application callback function for the receive   */
-                       /* look ahead                                               */
-                       this->RLength = MLength;
-#if defined(DIVA_ISTREAM)
-                       if ((a->rx_stream[this->Id] ||
-                            (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA)) &&
-                           ((Ind == N_DATA) ||
-                            (a->protocol_capabilities & PROTCAP_CMA_ALLPR))) {
-                               PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io;
-                               if (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA) {
-#if defined(DIVA_IDI_RX_DMA)
-                                       dword d;
-                                       diva_get_dma_map_entry(\
-                                               (struct _diva_dma_map_entry *)IoAdapter->dma_map,
-                                               (int)a->rx_stream[this->Id], (void **)&cma, &d);
-#else
-                                       cma = &a->stream_buffer[0];
-                                       cma[0] = cma[1] = cma[2] = cma[3] = 0;
-#endif
-                                       this->RLength = MLength = (word)*(dword *)cma;
-                                       cma += 4;
-                               } else {
-                                       int final = 0;
-                                       cma = &a->stream_buffer[0];
-                                       this->RLength = MLength = (word)diva_istream_read(a,
-                                                                                         Id,
-                                                                                         cma,
-                                                                                         sizeof(a->stream_buffer),
-                                                                                         &final, NULL, NULL);
-                               }
-                               IoAdapter->RBuffer.length = min(MLength, (word)270);
-                               if (IoAdapter->RBuffer.length != MLength) {
-                                       this->complete = 0;
-                               } else {
-                                       this->complete = 1;
-                               }
-                               memcpy(IoAdapter->RBuffer.P, cma, IoAdapter->RBuffer.length);
-                               this->RBuffer = (DBUFFER *)&IoAdapter->RBuffer;
-                       }
-#endif
-                       if (!cma) {
-                               a->ram_look_ahead(a, RBuffer, this);
-                       }
-                       this->RNum = 0;
-                       CALLBACK(a, this);
-                       /* map entity ptr, selector could be re-mapped by call to   */
-                       /* IDI from within callback                                 */
-                       this = entity_ptr(a, a->IdTable[Id]);
-                       xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind,
-                                    1/* rnr_valid */, this->RNR/* rnr */, a->IdTypeTable[this->No]);
-                       /* check for RNR                                            */
-                       if (this->RNR == 1) {
-                               this->RNR = 0;
-                               return 1;
-                       }
-                       /* if no buffers are provided by the application, the       */
-                       /* application want to copy the data itself including       */
-                       /* N_MDATA/LL_MDATA chaining                                */
-                       if (!this->RNR && !this->RNum) {
-                               xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind,
-                                            2/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
-                               return 0;
-                       }
-                       /* if there is no RNR, set the More flag                    */
-                       this->RCurrent = 0;
-                       this->ROffset = 0;
-               }
-               if (this->RNR == 2) {
-                       if (Ind != this->MInd) {
-                               this->RCurrent = 0xff;
-                               this->RNR = 0;
-                       }
-                       return 0;
-               }
-               /* if we have received buffers from the application, copy   */
-               /* the data into these buffers                              */
-               offset = 0;
-               R = PTR_R(a, this);
-               do {
-                       if (this->ROffset == R[this->RCurrent].PLength) {
-                               this->ROffset = 0;
-                               this->RCurrent++;
-                       }
-                       if (cma) {
-                               clength = min(MLength, (word)(R[this->RCurrent].PLength-this->ROffset));
-                       } else {
-                               clength = min(a->ram_inw(a, &RBuffer->length)-offset,
-                                             R[this->RCurrent].PLength-this->ROffset);
-                       }
-                       if (R[this->RCurrent].P) {
-                               if (cma) {
-                                       memcpy(PTR_P(a, this, &R[this->RCurrent].P[this->ROffset]),
-                                              &cma[offset],
-                                              clength);
-                               } else {
-                                       a->ram_in_buffer(a,
-                                                        &RBuffer->P[offset],
-                                                        PTR_P(a, this, &R[this->RCurrent].P[this->ROffset]),
-                                                        clength);
-                               }
-                       }
-                       offset += clength;
-                       this->ROffset += clength;
-                       if (cma) {
-                               if (offset >= MLength) {
-                                       break;
-                               }
-                               continue;
-                       }
-               } while (offset < (a->ram_inw(a, &RBuffer->length)));
-               /* if it's the last buffer of the packet, call the          */
-               /* application callback function for the receive complete   */
-               /* call                                                     */
-               if (Ind != this->MInd) {
-                       R[this->RCurrent].PLength = this->ROffset;
-                       if (this->ROffset) this->RCurrent++;
-                       this->RNum = this->RCurrent;
-                       this->RCurrent = 0xff;
-                       this->Ind = Ind;
-                       this->complete = 2;
-                       xdi_xlog_ind(XDI_A_NR(a), Id, Ch, Ind,
-                                    3/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
-                       CALLBACK(a, this);
-               }
-               return 0;
-       }
-       return 2;
-}
-#if defined(XDI_USE_XLOG)
-/* -----------------------------------------------------------
-   This function works in the same way as xlog on the
-   active board
-   ----------------------------------------------------------- */
-static void xdi_xlog(byte *msg, word code, int length) {
-       xdi_dbg_xlog("\x00\x02", msg, code, length);
-}
-#endif
-/* -----------------------------------------------------------
-   This function writes the information about the Return Code
-   processing in the trace buffer. Trace ID is 221.
-   INPUT:
-   Adapter - system unicue adapter number (0 ... 255)
-   Id      - Id of the entity that had sent this return code
-   Ch      - Channel of the entity that had sent this return code
-   Rc      - return code value
-   cb:       (0...2)
-   switch (cb) {
-   case 0: printf ("DELIVERY"); break;
-   case 1: printf ("CALLBACK"); break;
-   case 2: printf ("ASSIGN"); break;
-   }
-   DELIVERY - have entered isdn_rc with this RC
-   CALLBACK - about to make callback to the application
-   for this RC
-   ASSIGN   - about to make callback for RC that is result
-   of ASSIGN request. It is no DELIVERY message
-   before of this message
-   type   - the Id that was sent by the ASSIGN of this entity.
-   This should be global Id like NL_ID, DSIG_ID, MAN_ID.
-   An unknown Id will cause "?-" in the front of the request.
-   In this case the log.c is to be extended.
-   ----------------------------------------------------------- */
-static void xdi_xlog_rc_event(byte Adapter,
-                             byte Id, byte Ch, byte Rc, byte cb, byte type) {
-#if defined(XDI_USE_XLOG)
-       word LogInfo[4];
-       PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
-       PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
-       PUT_WORD(&LogInfo[2], ((word)Rc | (word)(type << 8)));
-       PUT_WORD(&LogInfo[3], cb);
-       xdi_xlog((byte *)&LogInfo[0], 221, sizeof(LogInfo));
-#endif
-}
-/* ------------------------------------------------------------------------
-   This function writes the information about the request processing
-   in the trace buffer. Trace ID is 220.
-   INPUT:
-   Adapter - system unicue adapter number (0 ... 255)
-   Id      - Id of the entity that had sent this request
-   Ch      - Channel of the entity that had sent this request
-   Req     - Code of the request
-   type    - the Id that was sent by the ASSIGN of this entity.
-   This should be global Id like NL_ID, DSIG_ID, MAN_ID.
-   An unknown Id will cause "?-" in the front of the request.
-   In this case the log.c is to be extended.
-   ------------------------------------------------------------------------ */
-static void xdi_xlog_request(byte Adapter, byte Id,
-                            byte Ch, byte Req, byte type) {
-#if defined(XDI_USE_XLOG)
-       word LogInfo[3];
-       PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
-       PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
-       PUT_WORD(&LogInfo[2], ((word)Req | (word)(type << 8)));
-       xdi_xlog((byte *)&LogInfo[0], 220, sizeof(LogInfo));
-#endif
-}
-/* ------------------------------------------------------------------------
-   This function writes the information about the indication processing
-   in the trace buffer. Trace ID is 222.
-   INPUT:
-   Adapter - system unicue adapter number (0 ... 255)
-   Id      - Id of the entity that had sent this indication
-   Ch      - Channel of the entity that had sent this indication
-   Ind     - Code of the indication
-   rnr_valid: (0 .. 3) supported
-   switch (rnr_valid) {
-   case 0: printf ("DELIVERY"); break;
-   case 1: printf ("RNR=%d", rnr);
-   case 2: printf ("RNum=0");
-   case 3: printf ("COMPLETE");
-   }
-   DELIVERY - indication entered isdn_rc function
-   RNR=...  - application had returned RNR=... after the
-   look ahead callback
-   RNum=0   - application had not returned any buffer to copy
-   this indication and will copy it self
-   COMPLETE - XDI had copied the data to the buffers provided
-   bu the application and is about to issue the
-   final callback
-   rnr:  Look case 1 of the rnr_valid
-   type: the Id that was sent by the ASSIGN of this entity. This should
-   be global Id like NL_ID, DSIG_ID, MAN_ID. An unknown Id will
-   cause "?-" in the front of the request. In this case the
-   log.c is to be extended.
-   ------------------------------------------------------------------------ */
-static void xdi_xlog_ind(byte Adapter,
-                        byte Id,
-                        byte Ch,
-                        byte Ind,
-                        byte rnr_valid,
-                        byte rnr,
-                        byte type) {
-#if defined(XDI_USE_XLOG)
-       word LogInfo[4];
-       PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
-       PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
-       PUT_WORD(&LogInfo[2], ((word)Ind | (word)(type << 8)));
-       PUT_WORD(&LogInfo[3], ((word)rnr | (word)(rnr_valid << 8)));
-       xdi_xlog((byte *)&LogInfo[0], 222, sizeof(LogInfo));
-#endif
-}
diff --git a/drivers/isdn/hardware/eicon/di.h b/drivers/isdn/hardware/eicon/di.h
deleted file mode 100644 (file)
index ff26c65..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-/*
- *  some macros for detailed trace management
- */
-#include "di_dbg.h"
-/*****************************************************************************/
-#define XMOREC 0x1f
-#define XMOREF 0x20
-#define XBUSY  0x40
-#define RMORE  0x80
-#define DIVA_MISC_FLAGS_REMOVE_PENDING    0x01
-#define DIVA_MISC_FLAGS_NO_RC_CANCELLING  0x02
-#define DIVA_MISC_FLAGS_RX_DMA            0x04
-/* structure for all information we have to keep on a per   */
-/* adapater basis                                           */
-typedef struct adapter_s ADAPTER;
-struct adapter_s {
-       void *io;
-       byte IdTable[256];
-       byte IdTypeTable[256];
-       byte FlowControlIdTable[256];
-       byte FlowControlSkipTable[256];
-       byte ReadyInt;
-       byte RcExtensionSupported;
-       byte misc_flags_table[256];
-       dword protocol_capabilities;
-       byte (*ram_in)(ADAPTER *a, void *adr);
-       word (*ram_inw)(ADAPTER *a, void *adr);
-       void (*ram_in_buffer)(ADAPTER *a, void *adr, void *P, word length);
-       void (*ram_look_ahead)(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
-       void (*ram_out)(ADAPTER *a, void *adr, byte data);
-       void (*ram_outw)(ADAPTER *a, void *adr, word data);
-       void (*ram_out_buffer)(ADAPTER *a, void *adr, void *P, word length);
-       void (*ram_inc)(ADAPTER *a, void *adr);
-#if defined(DIVA_ISTREAM)
-       dword rx_stream[256];
-       dword tx_stream[256];
-       word tx_pos[256];
-       word rx_pos[256];
-       byte stream_buffer[2512];
-       dword (*ram_offset)(ADAPTER *a);
-       void (*ram_out_dw)(ADAPTER *a,
-                          void *addr,
-                          const dword *data,
-                          int dwords);
-       void (*ram_in_dw)(ADAPTER *a,
-                         void *addr,
-                         dword *data,
-                         int dwords);
-       void (*istream_wakeup)(ADAPTER *a);
-#else
-       byte stream_buffer[4];
-#endif
-};
-/*------------------------------------------------------------------*/
-/* public functions of IDI common code                              */
-/*------------------------------------------------------------------*/
-void pr_out(ADAPTER *a);
-byte pr_dpc(ADAPTER *a);
-byte scom_test_int(ADAPTER *a);
-void scom_clear_int(ADAPTER *a);
-/*------------------------------------------------------------------*/
-/* OS specific functions used by IDI common code                    */
-/*------------------------------------------------------------------*/
-void free_entity(ADAPTER *a, byte e_no);
-void assign_queue(ADAPTER *a, byte e_no, word ref);
-byte get_assign(ADAPTER *a, word ref);
-void req_queue(ADAPTER *a, byte e_no);
-byte look_req(ADAPTER *a);
-void next_req(ADAPTER *a);
-ENTITY *entity_ptr(ADAPTER *a, byte e_no);
-#if defined(DIVA_ISTREAM)
-struct _diva_xdi_stream_interface;
-void diva_xdi_provide_istream_info(ADAPTER *a,
-                                  struct _diva_xdi_stream_interface *pI);
-void pr_stream(ADAPTER *a);
-int diva_istream_write(void *context,
-                      int Id,
-                      void *data,
-                      int length,
-                      int final,
-                      byte usr1,
-                      byte usr2);
-int diva_istream_read(void *context,
-                     int Id,
-                     void *data,
-                     int max_length,
-                     int *final,
-                     byte *usr1,
-                     byte *usr2);
-#if defined(DIVA_IDI_RX_DMA)
-#include "diva_dma.h"
-#endif
-#endif
diff --git a/drivers/isdn/hardware/eicon/di_dbg.h b/drivers/isdn/hardware/eicon/di_dbg.h
deleted file mode 100644 (file)
index 1380b60..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_DI_DBG_INC__
-#define __DIVA_DI_DBG_INC__
-#if !defined(dtrc)
-#define dtrc(a)
-#endif
-#if !defined(dbug)
-#define dbug(a)
-#endif
-#if !defined USE_EXTENDED_DEBUGS
-extern void (*dprintf)(char*, ...);
-#endif
-#endif
diff --git a/drivers/isdn/hardware/eicon/di_defs.h b/drivers/isdn/hardware/eicon/di_defs.h
deleted file mode 100644 (file)
index a5094d2..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef _DI_DEFS_
-#define _DI_DEFS_
-/* typedefs for our data structures                         */
-typedef struct get_name_s GET_NAME;
-/*  The entity_s structure is used to pass all
-    parameters between application and IDI   */
-typedef struct entity_s ENTITY;
-typedef struct buffers_s BUFFERS;
-typedef struct postcall_s POSTCALL;
-typedef struct get_para_s GET_PARA;
-#define BOARD_NAME_LENGTH 9
-#define IDI_CALL_LINK_T
-#define IDI_CALL_ENTITY_T
-/* typedef void ( * IDI_CALL)(ENTITY *); */
-/* --------------------------------------------------------
-   IDI_CALL
-   -------------------------------------------------------- */
-typedef void (IDI_CALL_LINK_T *IDI_CALL)(ENTITY IDI_CALL_ENTITY_T *);
-typedef struct {
-       word length;          /* length of data/parameter field           */
-       byte P[270];          /* data/parameter field                     */
-} DBUFFER;
-struct get_name_s {
-       word command;         /* command = 0x0100 */
-       byte name[BOARD_NAME_LENGTH];
-};
-struct postcall_s {
-       word      command;                           /* command = 0x0300 */
-       word      dummy;                             /* not used */
-       void      (*callback)(void *);      /* call back */
-       void      *context;                          /* context pointer */
-};
-#define REQ_PARA            0x0600   /* request command line parameters */
-#define REQ_PARA_LEN             1   /* number of data bytes */
-#define L1_STARTUP_DOWN_POS      0   /* '-y' command line parameter in......*/
-#define L1_STARTUP_DOWN_MSK   0x01   /* first byte position (index 0) with value 0x01 */
-struct get_para_s {
-       word  command;            /* command = 0x0600 */
-       byte  len;                /* max length of para field in bytes */
-       byte  para[REQ_PARA_LEN]; /* parameter field */
-};
-struct buffers_s {
-       word PLength;
-       byte *P;
-};
-struct entity_s {
-       byte                  Req;            /* pending request          */
-       byte                  Rc;             /* return code received     */
-       byte                  Ind;            /* indication received      */
-       byte                  ReqCh;          /* channel of current Req   */
-       byte                  RcCh;           /* channel of current Rc    */
-       byte                  IndCh;          /* channel of current Ind   */
-       byte                  Id;             /* ID used by this entity   */
-       byte                  GlobalId;       /* reserved field           */
-       byte                  XNum;           /* number of X-buffers      */
-       byte                  RNum;           /* number of R-buffers      */
-       BUFFERS               *X;             /* pointer to X-buffer list */
-       BUFFERS               *R;             /* pointer to R-buffer list */
-       word                  RLength;        /* length of current R-data */
-       DBUFFER               *RBuffer;       /* buffer of current R-data */
-       byte                  RNR;            /* receive not ready flag   */
-       byte                  complete;       /* receive complete status  */
-       IDI_CALL              callback;
-       word                  user[2];
-       /* fields used by the driver internally                     */
-       byte                  No;             /* entity number            */
-       byte                  reserved2;      /* reserved field           */
-       byte                  More;           /* R/X More flags           */
-       byte                  MInd;           /* MDATA coding for this ID */
-       byte                  XCurrent;       /* current transmit buffer  */
-       byte                  RCurrent;       /* current receive buffer   */
-       word                  XOffset;        /* offset in x-buffer       */
-       word                  ROffset;        /* offset in r-buffer       */
-};
-typedef struct {
-       byte                  type;
-       byte                  channels;
-       word                  features;
-       IDI_CALL              request;
-} DESCRIPTOR;
-/* descriptor type field coding */
-#define IDI_ADAPTER_S           1
-#define IDI_ADAPTER_PR          2
-#define IDI_ADAPTER_DIVA        3
-#define IDI_ADAPTER_MAESTRA     4
-#define IDI_VADAPTER            0x40
-#define IDI_DRIVER              0x80
-#define IDI_DADAPTER            0xfd
-#define IDI_DIDDPNP             0xfe
-#define IDI_DIMAINT             0xff
-/* Hardware IDs ISA PNP */
-#define HW_ID_DIVA_PRO     3    /* same as IDI_ADAPTER_DIVA    */
-#define HW_ID_MAESTRA      4    /* same as IDI_ADAPTER_MAESTRA */
-#define HW_ID_PICCOLA      5
-#define HW_ID_DIVA_PRO20   6
-#define HW_ID_DIVA20       7
-#define HW_ID_DIVA_PRO20_U 8
-#define HW_ID_DIVA20_U     9
-#define HW_ID_DIVA30       10
-#define HW_ID_DIVA30_U     11
-/* Hardware IDs PCI */
-#define HW_ID_EICON_PCI              0x1133
-#define HW_ID_SIEMENS_PCI            0x8001 /* unused SubVendor ID for Siemens Cornet-N cards */
-#define HW_ID_PROTTYPE_CORNETN       0x0014 /* SubDevice ID for Siemens Cornet-N cards */
-#define HW_ID_FUJITSU_SIEMENS_PCI    0x110A /* SubVendor ID for Fujitsu Siemens */
-#define HW_ID_GS03_PCI               0x0021 /* SubDevice ID for Fujitsu Siemens ISDN S0 card */
-#define HW_ID_DIVA_PRO20_PCI         0xe001
-#define HW_ID_DIVA20_PCI             0xe002
-#define HW_ID_DIVA_PRO20_PCI_U       0xe003
-#define HW_ID_DIVA20_PCI_U           0xe004
-#define HW_ID_DIVA201_PCI            0xe005
-#define HW_ID_DIVA_CT_ST             0xe006
-#define HW_ID_DIVA_CT_U              0xe007
-#define HW_ID_DIVA_CTL_ST            0xe008
-#define HW_ID_DIVA_CTL_U             0xe009
-#define HW_ID_DIVA_ISDN_V90_PCI      0xe00a
-#define HW_ID_DIVA202_PCI_ST         0xe00b
-#define HW_ID_DIVA202_PCI_U          0xe00c
-#define HW_ID_DIVA_PRO30_PCI         0xe00d
-#define HW_ID_MAESTRA_PCI            0xe010
-#define HW_ID_MAESTRAQ_PCI           0xe012
-#define HW_ID_DSRV_Q8M_V2_PCI        0xe013
-#define HW_ID_MAESTRAP_PCI           0xe014
-#define HW_ID_DSRV_P30M_V2_PCI       0xe015
-#define HW_ID_DSRV_VOICE_Q8M_PCI     0xe016
-#define HW_ID_DSRV_VOICE_Q8M_V2_PCI  0xe017
-#define HW_ID_DSRV_B2M_V2_PCI        0xe018
-#define HW_ID_DSRV_VOICE_P30M_V2_PCI 0xe019
-#define HW_ID_DSRV_B2F_PCI           0xe01a
-#define HW_ID_DSRV_VOICE_B2M_V2_PCI  0xe01b
-/* Hardware IDs USB */
-#define EICON_USB_VENDOR_ID          0x071D
-#define HW_ID_DIVA_USB_REV1          0x1000
-#define HW_ID_DIVA_USB_REV2          0x1003
-#define HW_ID_TELEDAT_SURF_USB_REV2  0x1004
-#define HW_ID_TELEDAT_SURF_USB_REV1  0x2000
-/* --------------------------------------------------------------------------
-   Adapter array change notification framework
-   -------------------------------------------------------------------------- */
-typedef void (IDI_CALL_LINK_T *didd_adapter_change_callback_t)(void IDI_CALL_ENTITY_T *context, DESCRIPTOR *adapter, int removal);
-/* -------------------------------------------------------------------------- */
-#define DI_VOICE          0x0 /* obsolete define */
-#define DI_FAX3           0x1
-#define DI_MODEM          0x2
-#define DI_POST           0x4
-#define DI_V110           0x8
-#define DI_V120           0x10
-#define DI_POTS           0x20
-#define DI_CODEC          0x40
-#define DI_MANAGE         0x80
-#define DI_V_42           0x0100
-#define DI_EXTD_FAX       0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
-#define DI_AT_PARSER      0x0400 /* Build-in AT Parser in the L2 */
-#define DI_VOICE_OVER_IP  0x0800 /* Voice over IP support */
-typedef void (IDI_CALL_LINK_T *_IDI_CALL)(void *, ENTITY *);
-#endif
diff --git a/drivers/isdn/hardware/eicon/did_vers.h b/drivers/isdn/hardware/eicon/did_vers.h
deleted file mode 100644 (file)
index fa8db82..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-static char diva_didd_common_code_build[] = "102-51";
diff --git a/drivers/isdn/hardware/eicon/diddfunc.c b/drivers/isdn/hardware/eicon/diddfunc.c
deleted file mode 100644 (file)
index b0b23ed..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/* $Id: diddfunc.c,v 1.14.6.2 2004/08/28 20:03:53 armin Exp $
- *
- * DIDD Interface module for Eicon active cards.
- *
- * Functions are in dadapter.c
- *
- * Copyright 2002-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include "platform.h"
-#include "di_defs.h"
-#include "dadapter.h"
-#include "divasync.h"
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-
-extern void DIVA_DIDD_Read(void *, int);
-extern char *DRIVERRELEASE_DIDD;
-static dword notify_handle;
-static DESCRIPTOR _DAdapter;
-
-/*
- * didd callback function
- */
-static void *didd_callback(void *context, DESCRIPTOR *adapter,
-                          int removal)
-{
-       if (adapter->type == IDI_DADAPTER) {
-               DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."))
-                       return (NULL);
-       } else if (adapter->type == IDI_DIMAINT) {
-               if (removal) {
-                       DbgDeregister();
-               } else {
-                       DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
-               }
-       }
-       return (NULL);
-}
-
-/*
- * connect to didd
- */
-static int __init connect_didd(void)
-{
-       int x = 0;
-       int dadapter = 0;
-       IDI_SYNC_REQ req;
-       DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
-
-       DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
-
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
-                       dadapter = 1;
-                       memcpy(&_DAdapter, &DIDD_Table[x], sizeof(_DAdapter));
-                       req.didd_notify.e.Req = 0;
-                       req.didd_notify.e.Rc =
-                               IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
-                       req.didd_notify.info.callback = (void *)didd_callback;
-                       req.didd_notify.info.context = NULL;
-                       _DAdapter.request((ENTITY *)&req);
-                       if (req.didd_notify.e.Rc != 0xff)
-                               return (0);
-                       notify_handle = req.didd_notify.info.handle;
-               } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
-                       DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
-               }
-       }
-       return (dadapter);
-}
-
-/*
- * disconnect from didd
- */
-static void __exit disconnect_didd(void)
-{
-       IDI_SYNC_REQ req;
-
-       req.didd_notify.e.Req = 0;
-       req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
-       req.didd_notify.info.handle = notify_handle;
-       _DAdapter.request((ENTITY *)&req);
-}
-
-/*
- * init
- */
-int __init diddfunc_init(void)
-{
-       diva_didd_load_time_init();
-
-       if (!connect_didd()) {
-               DBG_ERR(("init: failed to connect to DIDD."))
-                       diva_didd_load_time_finit();
-               return (0);
-       }
-       return (1);
-}
-
-/*
- * finit
- */
-void __exit diddfunc_finit(void)
-{
-       DbgDeregister();
-       disconnect_didd();
-       diva_didd_load_time_finit();
-}
diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c
deleted file mode 100644 (file)
index 1b25d8b..0000000
+++ /dev/null
@@ -1,666 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* $Id: diva.c,v 1.21.4.1 2004/05/08 14:33:43 armin Exp $ */
-
-#define CARDTYPE_H_WANT_DATA            1
-#define CARDTYPE_H_WANT_IDI_DATA        0
-#define CARDTYPE_H_WANT_RESOURCE_DATA   0
-#define CARDTYPE_H_WANT_FILE_DATA       0
-
-#include "platform.h"
-#include "debuglib.h"
-#include "cardtype.h"
-#include "pc.h"
-#include "di_defs.h"
-#include "di.h"
-#include "io.h"
-#include "pc_maint.h"
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "diva_pci.h"
-#include "diva.h"
-
-#ifdef CONFIG_ISDN_DIVAS_PRIPCI
-#include "os_pri.h"
-#endif
-#ifdef CONFIG_ISDN_DIVAS_BRIPCI
-#include "os_bri.h"
-#include "os_4bri.h"
-#endif
-
-PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
-extern IDI_CALL Requests[MAX_ADAPTER];
-extern int create_adapter_proc(diva_os_xdi_adapter_t *a);
-extern void remove_adapter_proc(diva_os_xdi_adapter_t *a);
-
-#define DivaIdiReqFunc(N)                                              \
-       static void DivaIdiRequest##N(ENTITY *e)                        \
-       { if (IoAdapters[N]) (*IoAdapters[N]->DIRequest)(IoAdapters[N], e); }
-
-/*
-**  Create own 32 Adapters
-*/
-DivaIdiReqFunc(0)
-DivaIdiReqFunc(1)
-DivaIdiReqFunc(2)
-DivaIdiReqFunc(3)
-DivaIdiReqFunc(4)
-DivaIdiReqFunc(5)
-DivaIdiReqFunc(6)
-DivaIdiReqFunc(7)
-DivaIdiReqFunc(8)
-DivaIdiReqFunc(9)
-DivaIdiReqFunc(10)
-DivaIdiReqFunc(11)
-DivaIdiReqFunc(12)
-DivaIdiReqFunc(13)
-DivaIdiReqFunc(14)
-DivaIdiReqFunc(15)
-DivaIdiReqFunc(16)
-DivaIdiReqFunc(17)
-DivaIdiReqFunc(18)
-DivaIdiReqFunc(19)
-DivaIdiReqFunc(20)
-DivaIdiReqFunc(21)
-DivaIdiReqFunc(22)
-DivaIdiReqFunc(23)
-DivaIdiReqFunc(24)
-DivaIdiReqFunc(25)
-DivaIdiReqFunc(26)
-DivaIdiReqFunc(27)
-DivaIdiReqFunc(28)
-DivaIdiReqFunc(29)
-DivaIdiReqFunc(30)
-DivaIdiReqFunc(31)
-
-/*
-**  LOCALS
-*/
-static LIST_HEAD(adapter_queue);
-
-typedef struct _diva_get_xlog {
-       word command;
-       byte req;
-       byte rc;
-       byte data[sizeof(struct mi_pc_maint)];
-} diva_get_xlog_t;
-
-typedef struct _diva_supported_cards_info {
-       int CardOrdinal;
-       diva_init_card_proc_t init_card;
-} diva_supported_cards_info_t;
-
-static diva_supported_cards_info_t divas_supported_cards[] = {
-#ifdef CONFIG_ISDN_DIVAS_PRIPCI
-       /*
-         PRI Cards
-       */
-       {CARDTYPE_DIVASRV_P_30M_PCI, diva_pri_init_card},
-       /*
-         PRI Rev.2 Cards
-       */
-       {CARDTYPE_DIVASRV_P_30M_V2_PCI, diva_pri_init_card},
-       /*
-         PRI Rev.2 VoIP Cards
-       */
-       {CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI, diva_pri_init_card},
-#endif
-#ifdef CONFIG_ISDN_DIVAS_BRIPCI
-       /*
-         4BRI Rev 1 Cards
-       */
-       {CARDTYPE_DIVASRV_Q_8M_PCI, diva_4bri_init_card},
-       {CARDTYPE_DIVASRV_VOICE_Q_8M_PCI, diva_4bri_init_card},
-       /*
-         4BRI Rev 2 Cards
-       */
-       {CARDTYPE_DIVASRV_Q_8M_V2_PCI, diva_4bri_init_card},
-       {CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI, diva_4bri_init_card},
-       /*
-         4BRI Based BRI Rev 2 Cards
-       */
-       {CARDTYPE_DIVASRV_B_2M_V2_PCI, diva_4bri_init_card},
-       {CARDTYPE_DIVASRV_B_2F_PCI, diva_4bri_init_card},
-       {CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI, diva_4bri_init_card},
-       /*
-         BRI
-       */
-       {CARDTYPE_MAESTRA_PCI, diva_bri_init_card},
-#endif
-
-       /*
-         EOL
-       */
-       {-1}
-};
-
-static void diva_init_request_array(void);
-static void *divas_create_pci_card(int handle, void *pci_dev_handle);
-
-static diva_os_spin_lock_t adapter_lock;
-
-static int diva_find_free_adapters(int base, int nr)
-{
-       int i;
-
-       for (i = 0; i < nr; i++) {
-               if (IoAdapters[base + i]) {
-                       return (-1);
-               }
-       }
-
-       return (0);
-}
-
-static diva_os_xdi_adapter_t *diva_q_get_next(struct list_head *what)
-{
-       diva_os_xdi_adapter_t *a = NULL;
-
-       if (what && (what->next != &adapter_queue))
-               a = list_entry(what->next, diva_os_xdi_adapter_t, link);
-
-       return (a);
-}
-
-/* --------------------------------------------------------------------------
-   Add card to the card list
-   -------------------------------------------------------------------------- */
-void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       diva_os_xdi_adapter_t *pdiva, *pa;
-       int i, j, max, nr;
-
-       for (i = 0; divas_supported_cards[i].CardOrdinal != -1; i++) {
-               if (divas_supported_cards[i].CardOrdinal == CardOrdinal) {
-                       if (!(pdiva = divas_create_pci_card(i, pdev))) {
-                               return NULL;
-                       }
-                       switch (CardOrdinal) {
-                       case CARDTYPE_DIVASRV_Q_8M_PCI:
-                       case CARDTYPE_DIVASRV_VOICE_Q_8M_PCI:
-                       case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
-                       case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
-                               max = MAX_ADAPTER - 4;
-                               nr = 4;
-                               break;
-
-                       default:
-                               max = MAX_ADAPTER;
-                               nr = 1;
-                       }
-
-                       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
-
-                       for (i = 0; i < max; i++) {
-                               if (!diva_find_free_adapters(i, nr)) {
-                                       pdiva->controller = i + 1;
-                                       pdiva->xdi_adapter.ANum = pdiva->controller;
-                                       IoAdapters[i] = &pdiva->xdi_adapter;
-                                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
-                                       create_adapter_proc(pdiva);     /* add adapter to proc file system */
-
-                                       DBG_LOG(("add %s:%d",
-                                                CardProperties
-                                                [CardOrdinal].Name,
-                                                pdiva->controller))
-
-                                               diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
-                                       pa = pdiva;
-                                       for (j = 1; j < nr; j++) {      /* slave adapters, if any */
-                                               pa = diva_q_get_next(&pa->link);
-                                               if (pa && !pa->interface.cleanup_adapter_proc) {
-                                                       pa->controller = i + 1 + j;
-                                                       pa->xdi_adapter.ANum = pa->controller;
-                                                       IoAdapters[i + j] = &pa->xdi_adapter;
-                                                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
-                                                       DBG_LOG(("add slave adapter (%d)",
-                                                                pa->controller))
-                                                               create_adapter_proc(pa);        /* add adapter to proc file system */
-                                                       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
-                                               } else {
-                                                       DBG_ERR(("slave adapter problem"))
-                                                               break;
-                                               }
-                                       }
-
-                                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
-                                       return (pdiva);
-                               }
-                       }
-
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
-
-                       /*
-                         Not able to add adapter - remove it and return error
-                       */
-                       DBG_ERR(("can not alloc request array"))
-                               diva_driver_remove_card(pdiva);
-
-                       return NULL;
-               }
-       }
-
-       return NULL;
-}
-
-/* --------------------------------------------------------------------------
-   Called on driver load, MAIN, main, DriverEntry
-   -------------------------------------------------------------------------- */
-int divasa_xdi_driver_entry(void)
-{
-       diva_os_initialize_spin_lock(&adapter_lock, "adapter");
-       memset(&IoAdapters[0], 0x00, sizeof(IoAdapters));
-       diva_init_request_array();
-
-       return (0);
-}
-
-/* --------------------------------------------------------------------------
-   Remove adapter from list
-   -------------------------------------------------------------------------- */
-static diva_os_xdi_adapter_t *get_and_remove_from_queue(void)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       diva_os_xdi_adapter_t *a = NULL;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "driver_unload");
-
-       if (!list_empty(&adapter_queue)) {
-               a = list_entry(adapter_queue.next, diva_os_xdi_adapter_t, link);
-               list_del(adapter_queue.next);
-       }
-
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
-       return (a);
-}
-
-/* --------------------------------------------------------------------------
-   Remove card from the card list
-   -------------------------------------------------------------------------- */
-void diva_driver_remove_card(void *pdiva)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       diva_os_xdi_adapter_t *a[4];
-       diva_os_xdi_adapter_t *pa;
-       int i;
-
-       pa = a[0] = (diva_os_xdi_adapter_t *) pdiva;
-       a[1] = a[2] = a[3] = NULL;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "remode adapter");
-
-       for (i = 1; i < 4; i++) {
-               if ((pa = diva_q_get_next(&pa->link))
-                   && !pa->interface.cleanup_adapter_proc) {
-                       a[i] = pa;
-               } else {
-                       break;
-               }
-       }
-
-       for (i = 0; ((i < 4) && a[i]); i++) {
-               list_del(&a[i]->link);
-       }
-
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
-
-       (*(a[0]->interface.cleanup_adapter_proc)) (a[0]);
-
-       for (i = 0; i < 4; i++) {
-               if (a[i]) {
-                       if (a[i]->controller) {
-                               DBG_LOG(("remove adapter (%d)",
-                                        a[i]->controller)) IoAdapters[a[i]->controller - 1] = NULL;
-                               remove_adapter_proc(a[i]);
-                       }
-                       diva_os_free(0, a[i]);
-               }
-       }
-}
-
-/* --------------------------------------------------------------------------
-   Create diva PCI adapter and init internal adapter structures
-   -------------------------------------------------------------------------- */
-static void *divas_create_pci_card(int handle, void *pci_dev_handle)
-{
-       diva_supported_cards_info_t *pI = &divas_supported_cards[handle];
-       diva_os_spin_lock_magic_t old_irql;
-       diva_os_xdi_adapter_t *a;
-
-       DBG_LOG(("found %d-%s", pI->CardOrdinal, CardProperties[pI->CardOrdinal].Name))
-
-               if (!(a = (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) {
-                       DBG_ERR(("A: can't alloc adapter"));
-                       return NULL;
-               }
-
-       memset(a, 0x00, sizeof(*a));
-
-       a->CardIndex = handle;
-       a->CardOrdinal = pI->CardOrdinal;
-       a->Bus = DIVAS_XDI_ADAPTER_BUS_PCI;
-       a->xdi_adapter.cardType = a->CardOrdinal;
-       a->resources.pci.bus = diva_os_get_pci_bus(pci_dev_handle);
-       a->resources.pci.func = diva_os_get_pci_func(pci_dev_handle);
-       a->resources.pci.hdev = pci_dev_handle;
-
-       /*
-         Add master adapter first, so slave adapters will receive higher
-         numbers as master adapter
-       */
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
-       list_add_tail(&a->link, &adapter_queue);
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
-
-       if ((*(pI->init_card)) (a)) {
-               diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
-               list_del(&a->link);
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
-               diva_os_free(0, a);
-               DBG_ERR(("A: can't get adapter resources"));
-               return NULL;
-       }
-
-       return (a);
-}
-
-/* --------------------------------------------------------------------------
-   Called on driver unload FINIT, finit, Unload
-   -------------------------------------------------------------------------- */
-void divasa_xdi_driver_unload(void)
-{
-       diva_os_xdi_adapter_t *a;
-
-       while ((a = get_and_remove_from_queue())) {
-               if (a->interface.cleanup_adapter_proc) {
-                       (*(a->interface.cleanup_adapter_proc)) (a);
-               }
-               if (a->controller) {
-                       IoAdapters[a->controller - 1] = NULL;
-                       remove_adapter_proc(a);
-               }
-               diva_os_free(0, a);
-       }
-       diva_os_destroy_spin_lock(&adapter_lock, "adapter");
-}
-
-/*
-**  Receive and process command from user mode utility
-*/
-void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
-                           int length, void *mptr,
-                           divas_xdi_copy_from_user_fn_t cp_fn)
-{
-       diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr;
-       diva_os_xdi_adapter_t *a = NULL;
-       diva_os_spin_lock_magic_t old_irql;
-       struct list_head *tmp;
-
-       if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
-               DBG_ERR(("A: A(?) open, msg too small (%d < %d)",
-                        length, sizeof(diva_xdi_um_cfg_cmd_t)))
-                       return NULL;
-       }
-       if ((*cp_fn) (os_handle, msg, src, sizeof(*msg)) <= 0) {
-               DBG_ERR(("A: A(?) open, write error"))
-                       return NULL;
-       }
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter");
-       list_for_each(tmp, &adapter_queue) {
-               a = list_entry(tmp, diva_os_xdi_adapter_t, link);
-               if (a->controller == (int)msg->adapter)
-                       break;
-               a = NULL;
-       }
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter");
-
-       if (!a) {
-               DBG_ERR(("A: A(%d) open, adapter not found", msg->adapter))
-                       }
-
-       return (a);
-}
-
-/*
-**  Easy cleanup mailbox status
-*/
-void diva_xdi_close_adapter(void *adapter, void *os_handle)
-{
-       diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
-
-       a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
-       if (a->xdi_mbox.data) {
-               diva_os_free(0, a->xdi_mbox.data);
-               a->xdi_mbox.data = NULL;
-       }
-}
-
-int
-diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
-              int length, void *mptr,
-              divas_xdi_copy_from_user_fn_t cp_fn)
-{
-       diva_xdi_um_cfg_cmd_t *msg = (diva_xdi_um_cfg_cmd_t *)mptr;
-       diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
-       void *data;
-
-       if (a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY) {
-               DBG_ERR(("A: A(%d) write, mbox busy", a->controller))
-                       return (-1);
-       }
-
-       if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
-               DBG_ERR(("A: A(%d) write, message too small (%d < %d)",
-                        a->controller, length,
-                        sizeof(diva_xdi_um_cfg_cmd_t)))
-                       return (-3);
-       }
-
-       if (!(data = diva_os_malloc(0, length))) {
-               DBG_ERR(("A: A(%d) write, ENOMEM", a->controller))
-                       return (-2);
-       }
-
-       if (msg) {
-               *(diva_xdi_um_cfg_cmd_t *)data = *msg;
-               length = (*cp_fn) (os_handle, (char *)data + sizeof(*msg),
-                                  src + sizeof(*msg), length - sizeof(*msg));
-       } else {
-               length = (*cp_fn) (os_handle, data, src, length);
-       }
-       if (length > 0) {
-               if ((*(a->interface.cmd_proc))
-                   (a, (diva_xdi_um_cfg_cmd_t *) data, length)) {
-                       length = -3;
-               }
-       } else {
-               DBG_ERR(("A: A(%d) write error (%d)", a->controller,
-                        length))
-                       }
-
-       diva_os_free(0, data);
-
-       return (length);
-}
-
-/*
-**  Write answers to user mode utility, if any
-*/
-int
-diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
-             int max_length, divas_xdi_copy_to_user_fn_t cp_fn)
-{
-       diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
-       int ret;
-
-       if (!(a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY)) {
-               DBG_ERR(("A: A(%d) rx mbox empty", a->controller))
-                       return (-1);
-       }
-       if (!a->xdi_mbox.data) {
-               a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
-               DBG_ERR(("A: A(%d) rx ENOMEM", a->controller))
-                       return (-2);
-       }
-
-       if (max_length < a->xdi_mbox.data_length) {
-               DBG_ERR(("A: A(%d) rx buffer too short(%d < %d)",
-                        a->controller, max_length,
-                        a->xdi_mbox.data_length))
-                       return (-3);
-       }
-
-       ret = (*cp_fn) (os_handle, dst, a->xdi_mbox.data,
-                       a->xdi_mbox.data_length);
-       if (ret > 0) {
-               diva_os_free(0, a->xdi_mbox.data);
-               a->xdi_mbox.data = NULL;
-               a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
-       }
-
-       return (ret);
-}
-
-
-irqreturn_t diva_os_irq_wrapper(int irq, void *context)
-{
-       diva_os_xdi_adapter_t *a = context;
-       diva_xdi_clear_interrupts_proc_t clear_int_proc;
-
-       if (!a || !a->xdi_adapter.diva_isr_handler)
-               return IRQ_NONE;
-
-       if ((clear_int_proc = a->clear_interrupts_proc)) {
-               (*clear_int_proc) (a);
-               a->clear_interrupts_proc = NULL;
-               return IRQ_HANDLED;
-       }
-
-       (*(a->xdi_adapter.diva_isr_handler)) (&a->xdi_adapter);
-       return IRQ_HANDLED;
-}
-
-static void diva_init_request_array(void)
-{
-       Requests[0] = DivaIdiRequest0;
-       Requests[1] = DivaIdiRequest1;
-       Requests[2] = DivaIdiRequest2;
-       Requests[3] = DivaIdiRequest3;
-       Requests[4] = DivaIdiRequest4;
-       Requests[5] = DivaIdiRequest5;
-       Requests[6] = DivaIdiRequest6;
-       Requests[7] = DivaIdiRequest7;
-       Requests[8] = DivaIdiRequest8;
-       Requests[9] = DivaIdiRequest9;
-       Requests[10] = DivaIdiRequest10;
-       Requests[11] = DivaIdiRequest11;
-       Requests[12] = DivaIdiRequest12;
-       Requests[13] = DivaIdiRequest13;
-       Requests[14] = DivaIdiRequest14;
-       Requests[15] = DivaIdiRequest15;
-       Requests[16] = DivaIdiRequest16;
-       Requests[17] = DivaIdiRequest17;
-       Requests[18] = DivaIdiRequest18;
-       Requests[19] = DivaIdiRequest19;
-       Requests[20] = DivaIdiRequest20;
-       Requests[21] = DivaIdiRequest21;
-       Requests[22] = DivaIdiRequest22;
-       Requests[23] = DivaIdiRequest23;
-       Requests[24] = DivaIdiRequest24;
-       Requests[25] = DivaIdiRequest25;
-       Requests[26] = DivaIdiRequest26;
-       Requests[27] = DivaIdiRequest27;
-       Requests[28] = DivaIdiRequest28;
-       Requests[29] = DivaIdiRequest29;
-       Requests[30] = DivaIdiRequest30;
-       Requests[31] = DivaIdiRequest31;
-}
-
-void diva_xdi_display_adapter_features(int card)
-{
-       dword features;
-       if (!card || ((card - 1) >= MAX_ADAPTER) || !IoAdapters[card - 1]) {
-               return;
-       }
-       card--;
-       features = IoAdapters[card]->Properties.Features;
-
-       DBG_LOG(("FEATURES FOR ADAPTER: %d", card + 1))
-               DBG_LOG((" DI_FAX3          :  %s",
-                        (features & DI_FAX3) ? "Y" : "N"))
-               DBG_LOG((" DI_MODEM         :  %s",
-                        (features & DI_MODEM) ? "Y" : "N"))
-               DBG_LOG((" DI_POST          :  %s",
-                        (features & DI_POST) ? "Y" : "N"))
-               DBG_LOG((" DI_V110          :  %s",
-                        (features & DI_V110) ? "Y" : "N"))
-               DBG_LOG((" DI_V120          :  %s",
-                        (features & DI_V120) ? "Y" : "N"))
-               DBG_LOG((" DI_POTS          :  %s",
-                        (features & DI_POTS) ? "Y" : "N"))
-               DBG_LOG((" DI_CODEC         :  %s",
-                        (features & DI_CODEC) ? "Y" : "N"))
-               DBG_LOG((" DI_MANAGE        :  %s",
-                        (features & DI_MANAGE) ? "Y" : "N"))
-               DBG_LOG((" DI_V_42          :  %s",
-                        (features & DI_V_42) ? "Y" : "N"))
-               DBG_LOG((" DI_EXTD_FAX      :  %s",
-                        (features & DI_EXTD_FAX) ? "Y" : "N"))
-               DBG_LOG((" DI_AT_PARSER     :  %s",
-                        (features & DI_AT_PARSER) ? "Y" : "N"))
-               DBG_LOG((" DI_VOICE_OVER_IP :  %s",
-                        (features & DI_VOICE_OVER_IP) ? "Y" : "N"))
-               }
-
-void diva_add_slave_adapter(diva_os_xdi_adapter_t *a)
-{
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add_slave");
-       list_add_tail(&a->link, &adapter_queue);
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add_slave");
-}
-
-int diva_card_read_xlog(diva_os_xdi_adapter_t *a)
-{
-       diva_get_xlog_t *req;
-       byte *data;
-
-       if (!a->xdi_adapter.Initialized || !a->xdi_adapter.DIRequest) {
-               return (-1);
-       }
-       if (!(data = diva_os_malloc(0, sizeof(struct mi_pc_maint)))) {
-               return (-1);
-       }
-       memset(data, 0x00, sizeof(struct mi_pc_maint));
-
-       if (!(req = diva_os_malloc(0, sizeof(*req)))) {
-               diva_os_free(0, data);
-               return (-1);
-       }
-       req->command = 0x0400;
-       req->req = LOG;
-       req->rc = 0x00;
-
-       (*(a->xdi_adapter.DIRequest)) (&a->xdi_adapter, (ENTITY *) req);
-
-       if (!req->rc || req->req) {
-               diva_os_free(0, data);
-               diva_os_free(0, req);
-               return (-1);
-       }
-
-       memcpy(data, &req->req, sizeof(struct mi_pc_maint));
-
-       diva_os_free(0, req);
-
-       a->xdi_mbox.data_length = sizeof(struct mi_pc_maint);
-       a->xdi_mbox.data = data;
-       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-
-       return (0);
-}
-
-void xdiFreeFile(void *handle)
-{
-}
diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h
deleted file mode 100644 (file)
index 1ad7665..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: diva.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
-
-#ifndef __DIVA_XDI_OS_PART_H__
-#define __DIVA_XDI_OS_PART_H__
-
-
-int divasa_xdi_driver_entry(void);
-void divasa_xdi_driver_unload(void);
-void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal);
-void diva_driver_remove_card(void *pdiva);
-
-typedef int (*divas_xdi_copy_to_user_fn_t) (void *os_handle, void __user *dst,
-                                           const void *src, int length);
-
-typedef int (*divas_xdi_copy_from_user_fn_t) (void *os_handle, void *dst,
-                                             const void __user *src, int length);
-
-int diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
-                 int max_length, divas_xdi_copy_to_user_fn_t cp_fn);
-
-int diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
-                  int length, void *msg,
-                  divas_xdi_copy_from_user_fn_t cp_fn);
-
-void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
-                           int length, void *msg,
-                           divas_xdi_copy_from_user_fn_t cp_fn);
-
-void diva_xdi_close_adapter(void *adapter, void *os_handle);
-
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/diva_didd.c b/drivers/isdn/hardware/eicon/diva_didd.c
deleted file mode 100644 (file)
index 60e7925..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/* $Id: diva_didd.c,v 1.13.6.4 2005/02/11 19:40:25 armin Exp $
- *
- * DIDD Interface module for Eicon active cards.
- *
- * Functions are in dadapter.c
- *
- * Copyright 2002-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <net/net_namespace.h>
-
-#include "platform.h"
-#include "di_defs.h"
-#include "dadapter.h"
-#include "divasync.h"
-#include "did_vers.h"
-
-static char *main_revision = "$Revision: 1.13.6.4 $";
-
-static char *DRIVERNAME =
-       "Eicon DIVA - DIDD table (http://www.melware.net)";
-static char *DRIVERLNAME = "divadidd";
-char *DRIVERRELEASE_DIDD = "2.0";
-
-MODULE_DESCRIPTION("DIDD table driver for diva drivers");
-MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
-MODULE_SUPPORTED_DEVICE("Eicon diva drivers");
-MODULE_LICENSE("GPL");
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-extern int diddfunc_init(void);
-extern void diddfunc_finit(void);
-
-extern void DIVA_DIDD_Read(void *, int);
-
-static struct proc_dir_entry *proc_didd;
-struct proc_dir_entry *proc_net_eicon = NULL;
-
-EXPORT_SYMBOL(DIVA_DIDD_Read);
-EXPORT_SYMBOL(proc_net_eicon);
-
-static char *getrev(const char *revision)
-{
-       char *rev;
-       char *p;
-       if ((p = strchr(revision, ':'))) {
-               rev = p + 2;
-               p = strchr(rev, '$');
-               *--p = 0;
-       } else
-               rev = "1.0";
-       return rev;
-}
-
-static int divadidd_proc_show(struct seq_file *m, void *v)
-{
-       char tmprev[32];
-
-       strcpy(tmprev, main_revision);
-       seq_printf(m, "%s\n", DRIVERNAME);
-       seq_printf(m, "name     : %s\n", DRIVERLNAME);
-       seq_printf(m, "release  : %s\n", DRIVERRELEASE_DIDD);
-       seq_printf(m, "build    : %s(%s)\n",
-                  diva_didd_common_code_build, DIVA_BUILD);
-       seq_printf(m, "revision : %s\n", getrev(tmprev));
-
-       return 0;
-}
-
-static int __init create_proc(void)
-{
-       proc_net_eicon = proc_mkdir("eicon", init_net.proc_net);
-
-       if (proc_net_eicon) {
-               proc_didd = proc_create_single(DRIVERLNAME, S_IRUGO,
-                               proc_net_eicon, divadidd_proc_show);
-               return (1);
-       }
-       return (0);
-}
-
-static void remove_proc(void)
-{
-       remove_proc_entry(DRIVERLNAME, proc_net_eicon);
-       remove_proc_entry("eicon", init_net.proc_net);
-}
-
-static int __init divadidd_init(void)
-{
-       char tmprev[32];
-       int ret = 0;
-
-       printk(KERN_INFO "%s\n", DRIVERNAME);
-       printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_DIDD);
-       strcpy(tmprev, main_revision);
-       printk("%s  Build:%s(%s)\n", getrev(tmprev),
-              diva_didd_common_code_build, DIVA_BUILD);
-
-       if (!create_proc()) {
-               printk(KERN_ERR "%s: could not create proc entry\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!diddfunc_init()) {
-               printk(KERN_ERR "%s: failed to connect to DIDD.\n",
-                      DRIVERLNAME);
-#ifdef MODULE
-               remove_proc();
-#endif
-               ret = -EIO;
-               goto out;
-       }
-
-out:
-       return (ret);
-}
-
-static void __exit divadidd_exit(void)
-{
-       diddfunc_finit();
-       remove_proc();
-       printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
-}
-
-module_init(divadidd_init);
-module_exit(divadidd_exit);
diff --git a/drivers/isdn/hardware/eicon/diva_dma.c b/drivers/isdn/hardware/eicon/diva_dma.c
deleted file mode 100644 (file)
index 217b6aa..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "diva_dma.h"
-/*
-  Every entry has length of PAGE_SIZE
-  and represents one single physical page
-*/
-struct _diva_dma_map_entry {
-       int busy;
-       dword phys_bus_addr;  /* 32bit address as seen by the card */
-       void *local_ram_addr; /* local address as seen by the host */
-       void *addr_handle;    /* handle uset to free allocated memory */
-};
-/*
-  Create local mapping structure and init it to default state
-*/
-struct _diva_dma_map_entry *diva_alloc_dma_map(void *os_context, int nentries) {
-       diva_dma_map_entry_t *pmap = diva_os_malloc(0, sizeof(*pmap) * (nentries + 1));
-       if (pmap)
-               memset(pmap, 0, sizeof(*pmap) * (nentries + 1));
-       return pmap;
-}
-/*
-  Free local map (context should be freed before) if any
-*/
-void diva_free_dma_mapping(struct _diva_dma_map_entry *pmap) {
-       if (pmap) {
-               diva_os_free(0, pmap);
-       }
-}
-/*
-  Set information saved on the map entry
-*/
-void diva_init_dma_map_entry(struct _diva_dma_map_entry *pmap,
-                            int nr, void *virt, dword phys,
-                            void *addr_handle) {
-       pmap[nr].phys_bus_addr  = phys;
-       pmap[nr].local_ram_addr = virt;
-       pmap[nr].addr_handle    = addr_handle;
-}
-/*
-  Allocate one single entry in the map
-*/
-int diva_alloc_dma_map_entry(struct _diva_dma_map_entry *pmap) {
-       int i;
-       for (i = 0; (pmap && pmap[i].local_ram_addr); i++) {
-               if (!pmap[i].busy) {
-                       pmap[i].busy = 1;
-                       return (i);
-               }
-       }
-       return (-1);
-}
-/*
-  Free one single entry in the map
-*/
-void diva_free_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr) {
-       pmap[nr].busy = 0;
-}
-/*
-  Get information saved on the map entry
-*/
-void diva_get_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr,
-                           void **pvirt, dword *pphys) {
-       *pphys = pmap[nr].phys_bus_addr;
-       *pvirt = pmap[nr].local_ram_addr;
-}
-void *diva_get_entry_handle(struct _diva_dma_map_entry *pmap, int nr) {
-       return (pmap[nr].addr_handle);
-}
diff --git a/drivers/isdn/hardware/eicon/diva_dma.h b/drivers/isdn/hardware/eicon/diva_dma.h
deleted file mode 100644 (file)
index d32c91b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_DMA_MAPPING_IFC_H__
-#define __DIVA_DMA_MAPPING_IFC_H__
-typedef struct _diva_dma_map_entry  diva_dma_map_entry_t;
-struct _diva_dma_map_entry *diva_alloc_dma_map(void *os_context, int nentries);
-void diva_init_dma_map_entry(struct _diva_dma_map_entry *pmap,
-                            int nr, void *virt, dword phys,
-                            void *addr_handle);
-int diva_alloc_dma_map_entry(struct _diva_dma_map_entry *pmap);
-void diva_free_dma_map_entry(struct _diva_dma_map_entry *pmap, int entry);
-void diva_get_dma_map_entry(struct _diva_dma_map_entry *pmap, int nr,
-                           void **pvirt, dword *pphys);
-void diva_free_dma_mapping(struct _diva_dma_map_entry *pmap);
-/*
-  Functionality to be implemented by OS wrapper
-  and running in process context
-*/
-void diva_init_dma_map(void *hdev,
-                      struct _diva_dma_map_entry **ppmap,
-                      int nentries);
-void diva_free_dma_map(void *hdev,
-                      struct _diva_dma_map_entry *pmap);
-void *diva_get_entry_handle(struct _diva_dma_map_entry *pmap, int nr);
-#endif
diff --git a/drivers/isdn/hardware/eicon/diva_pci.h b/drivers/isdn/hardware/eicon/diva_pci.h
deleted file mode 100644 (file)
index 7ef5db9..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: diva_pci.h,v 1.6 2003/01/04 15:29:45 schindler Exp $ */
-
-#ifndef __DIVA_PCI_INTERFACE_H__
-#define __DIVA_PCI_INTERFACE_H__
-
-void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a,
-                                  int id,
-                                  unsigned long bar,
-                                  unsigned long area_length);
-void divasa_unmap_pci_bar(void __iomem *bar);
-unsigned long divasa_get_pci_irq(unsigned char bus,
-                                unsigned char func, void *pci_dev_handle);
-unsigned long divasa_get_pci_bar(unsigned char bus,
-                                unsigned char func,
-                                int bar, void *pci_dev_handle);
-byte diva_os_get_pci_bus(void *pci_dev_handle);
-byte diva_os_get_pci_func(void *pci_dev_handle);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/divacapi.h b/drivers/isdn/hardware/eicon/divacapi.h
deleted file mode 100644 (file)
index c4868a0..0000000
+++ /dev/null
@@ -1,1350 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-/*#define DEBUG */
-
-#include <linux/types.h>
-
-#define IMPLEMENT_DTMF 1
-#define IMPLEMENT_LINE_INTERCONNECT2 1
-#define IMPLEMENT_ECHO_CANCELLER 1
-#define IMPLEMENT_RTP 1
-#define IMPLEMENT_T38 1
-#define IMPLEMENT_FAX_SUB_SEP_PWD 1
-#define IMPLEMENT_V18 1
-#define IMPLEMENT_DTMF_TONE 1
-#define IMPLEMENT_PIAFS 1
-#define IMPLEMENT_FAX_PAPER_FORMATS 1
-#define IMPLEMENT_VOWN 1
-#define IMPLEMENT_CAPIDTMF 1
-#define IMPLEMENT_FAX_NONSTANDARD 1
-#define VSWITCH_SUPPORT 1
-
-
-#define IMPLEMENT_LINE_INTERCONNECT 0
-#define IMPLEMENT_MARKED_OK_AFTER_FC 1
-
-#include "capidtmf.h"
-
-/*------------------------------------------------------------------*/
-/* Common API internal definitions                                  */
-/*------------------------------------------------------------------*/
-
-#define MAX_APPL 240
-#define MAX_NCCI           127
-
-#define MSG_IN_QUEUE_SIZE  ((4096 + 3) & 0xfffc)  /* must be multiple of 4 */
-
-
-#define MSG_IN_OVERHEAD    sizeof(APPL   *)
-
-#define MAX_NL_CHANNEL     255
-#define MAX_DATA_B3        8
-#define MAX_DATA_ACK       MAX_DATA_B3
-#define MAX_MULTI_IE       6
-#define MAX_MSG_SIZE       256
-#define MAX_MSG_PARMS      10
-#define MAX_CPN_MASK_SIZE  16
-#define MAX_MSN_CONFIG     10
-#define EXT_CONTROLLER     0x80
-#define CODEC              0x01
-#define CODEC_PERMANENT    0x02
-#define ADV_VOICE          0x03
-#define MAX_CIP_TYPES      5  /* kind of CIP types for group optimization */
-
-#define FAX_CONNECT_INFO_BUFFER_SIZE  256
-#define NCPI_BUFFER_SIZE              256
-
-#define MAX_CHANNELS_PER_PLCI         8
-#define MAX_INTERNAL_COMMAND_LEVELS   4
-#define INTERNAL_REQ_BUFFER_SIZE      272
-
-#define INTERNAL_IND_BUFFER_SIZE      768
-
-#define DTMF_PARAMETER_BUFFER_SIZE    12
-#define ADV_VOICE_COEF_BUFFER_SIZE    50
-
-#define LI_PLCI_B_QUEUE_ENTRIES       256
-
-
-
-typedef struct _APPL APPL;
-typedef struct _PLCI PLCI;
-typedef struct _NCCI NCCI;
-typedef struct _DIVA_CAPI_ADAPTER DIVA_CAPI_ADAPTER;
-typedef struct _DATA_B3_DESC DATA_B3_DESC;
-typedef struct _DATA_ACK_DESC DATA_ACK_DESC;
-typedef struct manufacturer_profile_s MANUFACTURER_PROFILE;
-typedef struct fax_ncpi_s FAX_NCPI;
-typedef struct api_parse_s API_PARSE;
-typedef struct api_save_s API_SAVE;
-typedef struct msn_config_s MSN_CONFIG;
-typedef struct msn_config_max_s MSN_CONFIG_MAX;
-typedef struct msn_ld_s MSN_LD;
-
-struct manufacturer_profile_s {
-       dword private_options;
-       dword rtp_primary_payloads;
-       dword rtp_additional_payloads;
-};
-
-struct fax_ncpi_s {
-       word options;
-       word format;
-};
-
-struct msn_config_s {
-       byte msn[MAX_CPN_MASK_SIZE];
-};
-
-struct msn_config_max_s {
-       MSN_CONFIG    msn_conf[MAX_MSN_CONFIG];
-};
-
-struct msn_ld_s {
-       dword low;
-       dword high;
-};
-
-struct api_parse_s {
-       word          length;
-       byte *info;
-};
-
-struct api_save_s {
-       API_PARSE     parms[MAX_MSG_PARMS + 1];
-       byte          info[MAX_MSG_SIZE];
-};
-
-struct _DATA_B3_DESC {
-       word          Handle;
-       word          Number;
-       word          Flags;
-       word          Length;
-       void *P;
-};
-
-struct _DATA_ACK_DESC {
-       word          Handle;
-       word          Number;
-};
-
-typedef void (*t_std_internal_command)(dword Id, PLCI *plci, byte Rc);
-
-/************************************************************************/
-/* Don't forget to adapt dos.asm after changing the _APPL structure!!!! */
-struct _APPL {
-       word          Id;
-       word          NullCREnable;
-       word          CDEnable;
-       dword         S_Handle;
-
-
-
-
-
-
-       LIST_ENTRY    s_function;
-       dword         s_context;
-       word          s_count;
-       APPL *s_next;
-       byte *xbuffer_used;
-       void **xbuffer_internal;
-       void **xbuffer_ptr;
-
-
-
-
-
-
-       byte *queue;
-       word          queue_size;
-       word          queue_free;
-       word          queue_read;
-       word          queue_write;
-       word          queue_signal;
-       byte          msg_lost;
-       byte          appl_flags;
-       word          Number;
-
-       word          MaxBuffer;
-       byte          MaxNCCI;
-       byte          MaxNCCIData;
-       word          MaxDataLength;
-       word          NCCIDataFlowCtrlTimer;
-       byte *ReceiveBuffer;
-       word *DataNCCI;
-       word *DataFlags;
-};
-
-
-struct _PLCI {
-       ENTITY        Sig;
-       ENTITY        NL;
-       word          RNum;
-       word          RFlags;
-       BUFFERS       RData[2];
-       BUFFERS       XData[1];
-       BUFFERS       NData[2];
-
-       DIVA_CAPI_ADAPTER   *adapter;
-       APPL      *appl;
-       PLCI      *relatedPTYPLCI;
-       byte          Id;
-       byte          State;
-       byte          sig_req;
-       byte          nl_req;
-       byte          SuppState;
-       byte          channels;
-       byte          tel;
-       byte          B1_resource;
-       byte          B2_prot;
-       byte          B3_prot;
-
-       word          command;
-       word          m_command;
-       word          internal_command;
-       word          number;
-       word          req_in_start;
-       word          req_in;
-       word          req_out;
-       word          msg_in_write_pos;
-       word          msg_in_read_pos;
-       word          msg_in_wrap_pos;
-
-       void *data_sent_ptr;
-       byte          data_sent;
-       byte          send_disc;
-       byte          sig_global_req;
-       byte          sig_remove_id;
-       byte          nl_global_req;
-       byte          nl_remove_id;
-       byte          b_channel;
-       byte          adv_nl;
-       byte          manufacturer;
-       byte          call_dir;
-       byte          hook_state;
-       byte          spoofed_msg;
-       byte          ptyState;
-       byte          cr_enquiry;
-       word          hangup_flow_ctrl_timer;
-
-       word          ncci_ring_list;
-       byte          inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI];
-       t_std_internal_command internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS];
-       DECLARE_BITMAP(c_ind_mask_table, MAX_APPL);
-       DECLARE_BITMAP(group_optimization_mask_table, MAX_APPL);
-       byte          RBuffer[200];
-       dword         msg_in_queue[MSG_IN_QUEUE_SIZE/sizeof(dword)];
-       API_SAVE      saved_msg;
-       API_SAVE      B_protocol;
-       byte          fax_connect_info_length;
-       byte          fax_connect_info_buffer[FAX_CONNECT_INFO_BUFFER_SIZE];
-       byte          fax_edata_ack_length;
-       word          nsf_control_bits;
-       byte          ncpi_state;
-       byte          ncpi_buffer[NCPI_BUFFER_SIZE];
-
-       byte          internal_req_buffer[INTERNAL_REQ_BUFFER_SIZE];
-       byte          internal_ind_buffer[INTERNAL_IND_BUFFER_SIZE + 3];
-       dword         requested_options_conn;
-       dword         requested_options;
-       word          B1_facilities;
-       API_SAVE   *adjust_b_parms_msg;
-       word          adjust_b_facilities;
-       word          adjust_b_command;
-       word          adjust_b_ncci;
-       word          adjust_b_mode;
-       word          adjust_b_state;
-       byte          adjust_b_restore;
-
-       byte          dtmf_rec_active;
-       word          dtmf_rec_pulse_ms;
-       word          dtmf_rec_pause_ms;
-       byte          dtmf_send_requests;
-       word          dtmf_send_pulse_ms;
-       word          dtmf_send_pause_ms;
-       word          dtmf_cmd;
-       word          dtmf_msg_number_queue[8];
-       byte          dtmf_parameter_length;
-       byte          dtmf_parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE];
-
-
-       t_capidtmf_state capidtmf_state;
-
-
-       byte          li_bchannel_id;    /* BRI: 1..2, PRI: 1..32 */
-       byte          li_channel_bits;
-       byte          li_notify_update;
-       word          li_cmd;
-       word          li_write_command;
-       word          li_write_channel;
-       word          li_plci_b_write_pos;
-       word          li_plci_b_read_pos;
-       word          li_plci_b_req_pos;
-       dword         li_plci_b_queue[LI_PLCI_B_QUEUE_ENTRIES];
-
-
-       word          ec_cmd;
-       word          ec_idi_options;
-       word          ec_tail_length;
-
-
-       byte          tone_last_indication_code;
-
-       byte          vswitchstate;
-       byte          vsprot;
-       byte          vsprotdialect;
-       byte          notifiedcall; /* Flag if it is a spoofed call */
-
-       int           rx_dma_descriptor;
-       dword         rx_dma_magic;
-};
-
-
-struct _NCCI {
-       byte          data_out;
-       byte          data_pending;
-       byte          data_ack_out;
-       byte          data_ack_pending;
-       DATA_B3_DESC  DBuffer[MAX_DATA_B3];
-       DATA_ACK_DESC DataAck[MAX_DATA_ACK];
-};
-
-
-struct _DIVA_CAPI_ADAPTER {
-       IDI_CALL      request;
-       byte          Id;
-       byte          max_plci;
-       byte          max_listen;
-       byte          listen_active;
-       PLCI      *plci;
-       byte          ch_ncci[MAX_NL_CHANNEL + 1];
-       byte          ncci_ch[MAX_NCCI + 1];
-       byte          ncci_plci[MAX_NCCI + 1];
-       byte          ncci_state[MAX_NCCI + 1];
-       byte          ncci_next[MAX_NCCI + 1];
-       NCCI          ncci[MAX_NCCI + 1];
-
-       byte          ch_flow_control[MAX_NL_CHANNEL + 1];  /* Used by XON protocol */
-       byte          ch_flow_control_pending;
-       byte          ch_flow_plci[MAX_NL_CHANNEL + 1];
-       int           last_flow_control_ch;
-
-       dword         Info_Mask[MAX_APPL];
-       dword         CIP_Mask[MAX_APPL];
-
-       dword         Notification_Mask[MAX_APPL];
-       PLCI      *codec_listen[MAX_APPL];
-       dword         requested_options_table[MAX_APPL];
-       API_PROFILE   profile;
-       MANUFACTURER_PROFILE man_profile;
-       dword         manufacturer_features;
-
-       byte          AdvCodecFLAG;
-       PLCI      *AdvCodecPLCI;
-       PLCI      *AdvSignalPLCI;
-       APPL      *AdvSignalAppl;
-       byte          TelOAD[23];
-       byte          TelOSA[23];
-       byte          scom_appl_disable;
-       PLCI      *automatic_lawPLCI;
-       byte          automatic_law;
-       byte          u_law;
-
-       byte          adv_voice_coef_length;
-       byte          adv_voice_coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE];
-
-       byte          li_pri;
-       byte          li_channels;
-       word          li_base;
-
-       byte adapter_disabled;
-       byte group_optimization_enabled; /* use application groups if enabled */
-       dword sdram_bar;
-       byte flag_dynamic_l1_down; /* for hunt groups:down layer 1 if no appl present*/
-       byte FlowControlIdTable[256];
-       byte FlowControlSkipTable[256];
-       void *os_card; /* pointer to associated OS dependent adapter structure */
-};
-
-
-/*------------------------------------------------------------------*/
-/* Application flags                                                */
-/*------------------------------------------------------------------*/
-
-#define APPL_FLAG_OLD_LI_SPEC           0x01
-#define APPL_FLAG_PRIV_EC_SPEC          0x02
-
-
-/*------------------------------------------------------------------*/
-/* API parameter definitions                                        */
-/*------------------------------------------------------------------*/
-
-#define X75_TTX         1       /* x.75 for ttx                     */
-#define TRF             2       /* transparent with hdlc framing    */
-#define TRF_IN          3       /* transparent with hdlc fr. inc.   */
-#define SDLC            4       /* sdlc, sna layer-2                */
-#define X75_BTX         5       /* x.75 for btx                     */
-#define LAPD            6       /* lapd (Q.921)                     */
-#define X25_L2          7       /* x.25 layer-2                     */
-#define V120_L2         8       /* V.120 layer-2 protocol           */
-#define V42_IN          9       /* V.42 layer-2 protocol, incoming */
-#define V42            10       /* V.42 layer-2 protocol            */
-#define MDM_ATP        11       /* AT Parser built in the L2        */
-#define X75_V42BIS     12       /* ISO7776 (X.75 SLP) modified to support V.42 bis compression */
-#define RTPL2_IN       13       /* RTP layer-2 protocol, incoming  */
-#define RTPL2          14       /* RTP layer-2 protocol             */
-#define V120_V42BIS    15       /* V.120 layer-2 protocol supporting V.42 bis compression */
-
-#define T70NL           1
-#define X25PLP          2
-#define T70NLX          3
-#define TRANSPARENT_NL  4
-#define ISO8208         5
-#define T30             6
-
-
-/*------------------------------------------------------------------*/
-/* FAX interface to IDI                                             */
-/*------------------------------------------------------------------*/
-
-#define CAPI_MAX_HEAD_LINE_SPACE        89
-#define CAPI_MAX_DATE_TIME_LENGTH       18
-
-#define T30_MAX_STATION_ID_LENGTH       20
-#define T30_MAX_SUBADDRESS_LENGTH       20
-#define T30_MAX_PASSWORD_LENGTH         20
-
-typedef struct t30_info_s T30_INFO;
-struct t30_info_s {
-       byte          code;
-       byte          rate_div_2400;
-       byte          resolution;
-       byte          data_format;
-       byte          pages_low;
-       byte          pages_high;
-       byte          operating_mode;
-       byte          control_bits_low;
-       byte          control_bits_high;
-       byte          feature_bits_low;
-       byte          feature_bits_high;
-       byte          recording_properties;
-       byte          universal_6;
-       byte          universal_7;
-       byte          station_id_len;
-       byte          head_line_len;
-       byte          station_id[T30_MAX_STATION_ID_LENGTH];
-/* byte          head_line[];      */
-/* byte          sub_sep_length;   */
-/* byte          sub_sep_field[];  */
-/* byte          pwd_length;       */
-/* byte          pwd_field[];      */
-/* byte          nsf_info_length;   */
-/* byte          nsf_info_field[];  */
-};
-
-
-#define T30_RESOLUTION_R8_0385          0x00
-#define T30_RESOLUTION_R8_0770_OR_200   0x01
-#define T30_RESOLUTION_R8_1540          0x02
-#define T30_RESOLUTION_R16_1540_OR_400  0x04
-#define T30_RESOLUTION_R4_0385_OR_100   0x08
-#define T30_RESOLUTION_300_300          0x10
-#define T30_RESOLUTION_INCH_BASED       0x40
-#define T30_RESOLUTION_METRIC_BASED     0x80
-
-#define T30_RECORDING_WIDTH_ISO_A4      0
-#define T30_RECORDING_WIDTH_ISO_B4      1
-#define T30_RECORDING_WIDTH_ISO_A3      2
-#define T30_RECORDING_WIDTH_COUNT       3
-
-#define T30_RECORDING_LENGTH_ISO_A4     0
-#define T30_RECORDING_LENGTH_ISO_B4     1
-#define T30_RECORDING_LENGTH_UNLIMITED  2
-#define T30_RECORDING_LENGTH_COUNT      3
-
-#define T30_MIN_SCANLINE_TIME_00_00_00  0
-#define T30_MIN_SCANLINE_TIME_05_05_05  1
-#define T30_MIN_SCANLINE_TIME_10_05_05  2
-#define T30_MIN_SCANLINE_TIME_10_10_10  3
-#define T30_MIN_SCANLINE_TIME_20_10_10  4
-#define T30_MIN_SCANLINE_TIME_20_20_20  5
-#define T30_MIN_SCANLINE_TIME_40_20_20  6
-#define T30_MIN_SCANLINE_TIME_40_40_40  7
-#define T30_MIN_SCANLINE_TIME_RES_8     8
-#define T30_MIN_SCANLINE_TIME_RES_9     9
-#define T30_MIN_SCANLINE_TIME_RES_10    10
-#define T30_MIN_SCANLINE_TIME_10_10_05  11
-#define T30_MIN_SCANLINE_TIME_20_10_05  12
-#define T30_MIN_SCANLINE_TIME_20_20_10  13
-#define T30_MIN_SCANLINE_TIME_40_20_10  14
-#define T30_MIN_SCANLINE_TIME_40_40_20  15
-#define T30_MIN_SCANLINE_TIME_COUNT     16
-
-#define T30_DATA_FORMAT_SFF             0
-#define T30_DATA_FORMAT_ASCII           1
-#define T30_DATA_FORMAT_NATIVE          2
-#define T30_DATA_FORMAT_COUNT           3
-
-
-#define T30_OPERATING_MODE_STANDARD     0
-#define T30_OPERATING_MODE_CLASS2       1
-#define T30_OPERATING_MODE_CLASS1       2
-#define T30_OPERATING_MODE_CAPI         3
-#define T30_OPERATING_MODE_CAPI_NEG     4
-#define T30_OPERATING_MODE_COUNT        5
-
-/* EDATA transmit messages */
-#define EDATA_T30_DIS         0x01
-#define EDATA_T30_FTT         0x02
-#define EDATA_T30_MCF         0x03
-#define EDATA_T30_PARAMETERS  0x04
-
-/* EDATA receive messages */
-#define EDATA_T30_DCS         0x81
-#define EDATA_T30_TRAIN_OK    0x82
-#define EDATA_T30_EOP         0x83
-#define EDATA_T30_MPS         0x84
-#define EDATA_T30_EOM         0x85
-#define EDATA_T30_DTC         0x86
-#define EDATA_T30_PAGE_END    0x87   /* Indicates end of page data. Reserved, but not implemented ! */
-#define EDATA_T30_EOP_CAPI    0x88
-
-
-#define T30_SUCCESS                        0
-#define T30_ERR_NO_DIS_RECEIVED            1
-#define T30_ERR_TIMEOUT_NO_RESPONSE        2
-#define T30_ERR_RETRY_NO_RESPONSE          3
-#define T30_ERR_TOO_MANY_REPEATS           4
-#define T30_ERR_UNEXPECTED_MESSAGE         5
-#define T30_ERR_UNEXPECTED_DCN             6
-#define T30_ERR_DTC_UNSUPPORTED            7
-#define T30_ERR_ALL_RATES_FAILED           8
-#define T30_ERR_TOO_MANY_TRAINS            9
-#define T30_ERR_RECEIVE_CORRUPTED          10
-#define T30_ERR_UNEXPECTED_DISC            11
-#define T30_ERR_APPLICATION_DISC           12
-#define T30_ERR_INCOMPATIBLE_DIS           13
-#define T30_ERR_INCOMPATIBLE_DCS           14
-#define T30_ERR_TIMEOUT_NO_COMMAND         15
-#define T30_ERR_RETRY_NO_COMMAND           16
-#define T30_ERR_TIMEOUT_COMMAND_TOO_LONG   17
-#define T30_ERR_TIMEOUT_RESPONSE_TOO_LONG  18
-#define T30_ERR_NOT_IDENTIFIED             19
-#define T30_ERR_SUPERVISORY_TIMEOUT        20
-#define T30_ERR_TOO_LONG_SCAN_LINE         21
-/* #define T30_ERR_RETRY_NO_PAGE_AFTER_MPS    22 */
-#define T30_ERR_RETRY_NO_PAGE_RECEIVED     23
-#define T30_ERR_RETRY_NO_DCS_AFTER_FTT     24
-#define T30_ERR_RETRY_NO_DCS_AFTER_EOM     25
-#define T30_ERR_RETRY_NO_DCS_AFTER_MPS     26
-#define T30_ERR_RETRY_NO_DCN_AFTER_MCF     27
-#define T30_ERR_RETRY_NO_DCN_AFTER_RTN     28
-#define T30_ERR_RETRY_NO_CFR               29
-#define T30_ERR_RETRY_NO_MCF_AFTER_EOP     30
-#define T30_ERR_RETRY_NO_MCF_AFTER_EOM     31
-#define T30_ERR_RETRY_NO_MCF_AFTER_MPS     32
-#define T30_ERR_SUB_SEP_UNSUPPORTED        33
-#define T30_ERR_PWD_UNSUPPORTED            34
-#define T30_ERR_SUB_SEP_PWD_UNSUPPORTED    35
-#define T30_ERR_INVALID_COMMAND_FRAME      36
-#define T30_ERR_UNSUPPORTED_PAGE_CODING    37
-#define T30_ERR_INVALID_PAGE_CODING        38
-#define T30_ERR_INCOMPATIBLE_PAGE_CONFIG   39
-#define T30_ERR_TIMEOUT_FROM_APPLICATION   40
-#define T30_ERR_V34FAX_NO_REACTION_ON_MARK 41
-#define T30_ERR_V34FAX_TRAINING_TIMEOUT    42
-#define T30_ERR_V34FAX_UNEXPECTED_V21      43
-#define T30_ERR_V34FAX_PRIMARY_CTS_ON      44
-#define T30_ERR_V34FAX_TURNAROUND_POLLING  45
-#define T30_ERR_V34FAX_V8_INCOMPATIBILITY  46
-
-
-#define T30_CONTROL_BIT_DISABLE_FINE       0x0001
-#define T30_CONTROL_BIT_ENABLE_ECM         0x0002
-#define T30_CONTROL_BIT_ECM_64_BYTES       0x0004
-#define T30_CONTROL_BIT_ENABLE_2D_CODING   0x0008
-#define T30_CONTROL_BIT_ENABLE_T6_CODING   0x0010
-#define T30_CONTROL_BIT_ENABLE_UNCOMPR     0x0020
-#define T30_CONTROL_BIT_ACCEPT_POLLING     0x0040
-#define T30_CONTROL_BIT_REQUEST_POLLING    0x0080
-#define T30_CONTROL_BIT_MORE_DOCUMENTS     0x0100
-#define T30_CONTROL_BIT_ACCEPT_SUBADDRESS  0x0200
-#define T30_CONTROL_BIT_ACCEPT_SEL_POLLING 0x0400
-#define T30_CONTROL_BIT_ACCEPT_PASSWORD    0x0800
-#define T30_CONTROL_BIT_ENABLE_V34FAX      0x1000
-#define T30_CONTROL_BIT_EARLY_CONNECT      0x2000
-
-#define T30_CONTROL_BIT_ALL_FEATURES  (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |   T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR |   T30_CONTROL_BIT_ENABLE_V34FAX)
-
-#define T30_FEATURE_BIT_FINE               0x0001
-#define T30_FEATURE_BIT_ECM                0x0002
-#define T30_FEATURE_BIT_ECM_64_BYTES       0x0004
-#define T30_FEATURE_BIT_2D_CODING          0x0008
-#define T30_FEATURE_BIT_T6_CODING          0x0010
-#define T30_FEATURE_BIT_UNCOMPR_ENABLED    0x0020
-#define T30_FEATURE_BIT_POLLING            0x0040
-#define T30_FEATURE_BIT_MORE_DOCUMENTS     0x0100
-#define T30_FEATURE_BIT_V34FAX             0x1000
-
-
-#define T30_NSF_CONTROL_BIT_ENABLE_NSF     0x0001
-#define T30_NSF_CONTROL_BIT_RAW_INFO       0x0002
-#define T30_NSF_CONTROL_BIT_NEGOTIATE_IND  0x0004
-#define T30_NSF_CONTROL_BIT_NEGOTIATE_RESP 0x0008
-
-#define T30_NSF_ELEMENT_NSF_FIF            0x00
-#define T30_NSF_ELEMENT_NSC_FIF            0x01
-#define T30_NSF_ELEMENT_NSS_FIF            0x02
-#define T30_NSF_ELEMENT_COMPANY_NAME       0x03
-
-
-/*------------------------------------------------------------------*/
-/* Analog modem definitions                                         */
-/*------------------------------------------------------------------*/
-
-typedef struct async_s ASYNC_FORMAT;
-struct async_s {
-       unsigned pe:1;
-       unsigned parity:2;
-       unsigned spare:2;
-       unsigned stp:1;
-       unsigned ch_len:2;   /* 3th octett in CAI */
-};
-
-
-/*------------------------------------------------------------------*/
-/* PLCI/NCCI states                                                 */
-/*------------------------------------------------------------------*/
-
-#define IDLE                    0
-#define OUTG_CON_PENDING        1
-#define INC_CON_PENDING         2
-#define INC_CON_ALERT           3
-#define INC_CON_ACCEPT          4
-#define INC_ACT_PENDING         5
-#define LISTENING               6
-#define CONNECTED               7
-#define OUTG_DIS_PENDING        8
-#define INC_DIS_PENDING         9
-#define LOCAL_CONNECT           10
-#define INC_RES_PENDING         11
-#define OUTG_RES_PENDING        12
-#define SUSPENDING              13
-#define ADVANCED_VOICE_SIG      14
-#define ADVANCED_VOICE_NOSIG    15
-#define RESUMING                16
-#define INC_CON_CONNECTED_ALERT 17
-#define OUTG_REJ_PENDING        18
-
-
-/*------------------------------------------------------------------*/
-/* auxiliary states for supplementary services                     */
-/*------------------------------------------------------------------*/
-
-#define IDLE                0
-#define HOLD_REQUEST        1
-#define HOLD_INDICATE       2
-#define CALL_HELD           3
-#define RETRIEVE_REQUEST    4
-#define RETRIEVE_INDICATION 5
-
-/*------------------------------------------------------------------*/
-/* Capi IE + Msg types                                              */
-/*------------------------------------------------------------------*/
-#define ESC_CAUSE        0x800 | CAU        /* Escape cause element */
-#define ESC_MSGTYPE      0x800 | MSGTYPEIE  /* Escape message type  */
-#define ESC_CHI          0x800 | CHI        /* Escape channel id    */
-#define ESC_LAW          0x800 | BC         /* Escape law info      */
-#define ESC_CR           0x800 | CRIE       /* Escape CallReference */
-#define ESC_PROFILE      0x800 | PROFILEIE  /* Escape profile       */
-#define ESC_SSEXT        0x800 | SSEXTIE    /* Escape Supplem. Serv.*/
-#define ESC_VSWITCH      0x800 | VSWITCHIE  /* Escape VSwitch       */
-#define CST              0x14               /* Call State i.e.      */
-#define PI               0x1E               /* Progress Indicator   */
-#define NI               0x27               /* Notification Ind     */
-#define CONN_NR          0x4C               /* Connected Number     */
-#define CONG_RNR         0xBF               /* Congestion RNR       */
-#define CONG_RR          0xB0               /* Congestion RR        */
-#define RESERVED         0xFF               /* Res. for future use  */
-#define ON_BOARD_CODEC   0x02               /* external controller  */
-#define HANDSET          0x04               /* Codec+Handset(Pro11) */
-#define HOOK_SUPPORT     0x01               /* activate Hook signal */
-#define SCR              0x7a               /* unscreened number    */
-
-#define HOOK_OFF_REQ     0x9001             /* internal conn req    */
-#define HOOK_ON_REQ      0x9002             /* internal disc req    */
-#define SUSPEND_REQ      0x9003             /* internal susp req    */
-#define RESUME_REQ       0x9004             /* internal resume req  */
-#define USELAW_REQ       0x9005             /* internal law    req  */
-#define LISTEN_SIG_ASSIGN_PEND  0x9006
-#define PERM_LIST_REQ    0x900a             /* permanent conn DCE   */
-#define C_HOLD_REQ       0x9011
-#define C_RETRIEVE_REQ   0x9012
-#define C_NCR_FAC_REQ    0x9013
-#define PERM_COD_ASSIGN  0x9014
-#define PERM_COD_CALL    0x9015
-#define PERM_COD_HOOK    0x9016
-#define PERM_COD_CONN_PEND 0x9017           /* wait for connect_con */
-#define PTY_REQ_PEND     0x9018
-#define CD_REQ_PEND      0x9019
-#define CF_START_PEND    0x901a
-#define CF_STOP_PEND     0x901b
-#define ECT_REQ_PEND     0x901c
-#define GETSERV_REQ_PEND 0x901d
-#define BLOCK_PLCI       0x901e
-#define INTERR_NUMBERS_REQ_PEND         0x901f
-#define INTERR_DIVERSION_REQ_PEND       0x9020
-#define MWI_ACTIVATE_REQ_PEND           0x9021
-#define MWI_DEACTIVATE_REQ_PEND         0x9022
-#define SSEXT_REQ_COMMAND               0x9023
-#define SSEXT_NC_REQ_COMMAND            0x9024
-#define START_L1_SIG_ASSIGN_PEND        0x9025
-#define REM_L1_SIG_ASSIGN_PEND          0x9026
-#define CONF_BEGIN_REQ_PEND             0x9027
-#define CONF_ADD_REQ_PEND               0x9028
-#define CONF_SPLIT_REQ_PEND             0x9029
-#define CONF_DROP_REQ_PEND              0x902a
-#define CONF_ISOLATE_REQ_PEND           0x902b
-#define CONF_REATTACH_REQ_PEND          0x902c
-#define VSWITCH_REQ_PEND                0x902d
-#define GET_MWI_STATE                   0x902e
-#define CCBS_REQUEST_REQ_PEND           0x902f
-#define CCBS_DEACTIVATE_REQ_PEND        0x9030
-#define CCBS_INTERROGATE_REQ_PEND       0x9031
-
-#define NO_INTERNAL_COMMAND             0
-#define DTMF_COMMAND_1                  1
-#define DTMF_COMMAND_2                  2
-#define DTMF_COMMAND_3                  3
-#define MIXER_COMMAND_1                 4
-#define MIXER_COMMAND_2                 5
-#define MIXER_COMMAND_3                 6
-#define ADV_VOICE_COMMAND_CONNECT_1     7
-#define ADV_VOICE_COMMAND_CONNECT_2     8
-#define ADV_VOICE_COMMAND_CONNECT_3     9
-#define ADV_VOICE_COMMAND_DISCONNECT_1  10
-#define ADV_VOICE_COMMAND_DISCONNECT_2  11
-#define ADV_VOICE_COMMAND_DISCONNECT_3  12
-#define ADJUST_B_RESTORE_1              13
-#define ADJUST_B_RESTORE_2              14
-#define RESET_B3_COMMAND_1              15
-#define SELECT_B_COMMAND_1              16
-#define FAX_CONNECT_INFO_COMMAND_1      17
-#define FAX_CONNECT_INFO_COMMAND_2      18
-#define FAX_ADJUST_B23_COMMAND_1        19
-#define FAX_ADJUST_B23_COMMAND_2        20
-#define EC_COMMAND_1                    21
-#define EC_COMMAND_2                    22
-#define EC_COMMAND_3                    23
-#define RTP_CONNECT_B3_REQ_COMMAND_1    24
-#define RTP_CONNECT_B3_REQ_COMMAND_2    25
-#define RTP_CONNECT_B3_REQ_COMMAND_3    26
-#define RTP_CONNECT_B3_RES_COMMAND_1    27
-#define RTP_CONNECT_B3_RES_COMMAND_2    28
-#define RTP_CONNECT_B3_RES_COMMAND_3    29
-#define HOLD_SAVE_COMMAND_1             30
-#define RETRIEVE_RESTORE_COMMAND_1      31
-#define FAX_DISCONNECT_COMMAND_1        32
-#define FAX_DISCONNECT_COMMAND_2        33
-#define FAX_DISCONNECT_COMMAND_3        34
-#define FAX_EDATA_ACK_COMMAND_1         35
-#define FAX_EDATA_ACK_COMMAND_2         36
-#define FAX_CONNECT_ACK_COMMAND_1       37
-#define FAX_CONNECT_ACK_COMMAND_2       38
-#define STD_INTERNAL_COMMAND_COUNT      39
-
-#define UID              0x2d               /* User Id for Mgmt      */
-
-#define CALL_DIR_OUT             0x01       /* call direction of initial call */
-#define CALL_DIR_IN              0x02
-#define CALL_DIR_ORIGINATE       0x04       /* DTE/DCE direction according to */
-#define CALL_DIR_ANSWER          0x08       /*   state of B-Channel Operation */
-#define CALL_DIR_FORCE_OUTG_NL   0x10       /* for RESET_B3 reconnect, after DISC_B3... */
-
-#define AWAITING_MANUF_CON 0x80             /* command spoofing flags */
-#define SPOOFING_REQUIRED  0xff
-#define AWAITING_SELECT_B  0xef
-
-/*------------------------------------------------------------------*/
-/* B_CTRL / DSP_CTRL                                                */
-/*------------------------------------------------------------------*/
-
-#define DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS     0x01
-#define DSP_CTRL_SET_BCHANNEL_PASSIVATION_BRI   0x02
-#define DSP_CTRL_SET_DTMF_PARAMETERS            0x03
-
-#define MANUFACTURER_FEATURE_SLAVE_CODEC          0x00000001L
-#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS   0x00000002L
-#define MANUFACTURER_FEATURE_HARDDTMF             0x00000004L
-#define MANUFACTURER_FEATURE_SOFTDTMF_SEND        0x00000008L
-#define MANUFACTURER_FEATURE_DTMF_PARAMETERS      0x00000010L
-#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE     0x00000020L
-#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD      0x00000040L
-#define MANUFACTURER_FEATURE_V18                  0x00000080L
-#define MANUFACTURER_FEATURE_MIXER_CH_CH          0x00000100L
-#define MANUFACTURER_FEATURE_MIXER_CH_PC          0x00000200L
-#define MANUFACTURER_FEATURE_MIXER_PC_CH          0x00000400L
-#define MANUFACTURER_FEATURE_MIXER_PC_PC          0x00000800L
-#define MANUFACTURER_FEATURE_ECHO_CANCELLER       0x00001000L
-#define MANUFACTURER_FEATURE_RTP                  0x00002000L
-#define MANUFACTURER_FEATURE_T38                  0x00004000L
-#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
-#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL  0x00010000L
-#define MANUFACTURER_FEATURE_OOB_CHANNEL          0x00020000L
-#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL      0x00040000L
-#define MANUFACTURER_FEATURE_IN_BAND_FEATURE      0x00080000L
-#define MANUFACTURER_FEATURE_PIAFS                0x00100000L
-#define MANUFACTURER_FEATURE_DTMF_TONE            0x00200000L
-#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS    0x00400000L
-#define MANUFACTURER_FEATURE_OK_FC_LABEL          0x00800000L
-#define MANUFACTURER_FEATURE_VOWN                 0x01000000L
-#define MANUFACTURER_FEATURE_XCONNECT             0x02000000L
-#define MANUFACTURER_FEATURE_DMACONNECT           0x04000000L
-#define MANUFACTURER_FEATURE_AUDIO_TAP            0x08000000L
-#define MANUFACTURER_FEATURE_FAX_NONSTANDARD      0x10000000L
-
-/*------------------------------------------------------------------*/
-/* DTMF interface to IDI                                            */
-/*------------------------------------------------------------------*/
-
-
-#define DTMF_DIGIT_TONE_LOW_GROUP_697_HZ        0x00
-#define DTMF_DIGIT_TONE_LOW_GROUP_770_HZ        0x01
-#define DTMF_DIGIT_TONE_LOW_GROUP_852_HZ        0x02
-#define DTMF_DIGIT_TONE_LOW_GROUP_941_HZ        0x03
-#define DTMF_DIGIT_TONE_LOW_GROUP_MASK          0x03
-#define DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ      0x00
-#define DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ      0x04
-#define DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ      0x08
-#define DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ      0x0c
-#define DTMF_DIGIT_TONE_HIGH_GROUP_MASK         0x0c
-#define DTMF_DIGIT_TONE_CODE_0                  0x07
-#define DTMF_DIGIT_TONE_CODE_1                  0x00
-#define DTMF_DIGIT_TONE_CODE_2                  0x04
-#define DTMF_DIGIT_TONE_CODE_3                  0x08
-#define DTMF_DIGIT_TONE_CODE_4                  0x01
-#define DTMF_DIGIT_TONE_CODE_5                  0x05
-#define DTMF_DIGIT_TONE_CODE_6                  0x09
-#define DTMF_DIGIT_TONE_CODE_7                  0x02
-#define DTMF_DIGIT_TONE_CODE_8                  0x06
-#define DTMF_DIGIT_TONE_CODE_9                  0x0a
-#define DTMF_DIGIT_TONE_CODE_STAR               0x03
-#define DTMF_DIGIT_TONE_CODE_HASHMARK           0x0b
-#define DTMF_DIGIT_TONE_CODE_A                  0x0c
-#define DTMF_DIGIT_TONE_CODE_B                  0x0d
-#define DTMF_DIGIT_TONE_CODE_C                  0x0e
-#define DTMF_DIGIT_TONE_CODE_D                  0x0f
-
-#define DTMF_UDATA_REQUEST_SEND_DIGITS            16
-#define DTMF_UDATA_REQUEST_ENABLE_RECEIVER        17
-#define DTMF_UDATA_REQUEST_DISABLE_RECEIVER       18
-#define DTMF_UDATA_INDICATION_DIGITS_SENT         16
-#define DTMF_UDATA_INDICATION_DIGITS_RECEIVED     17
-#define DTMF_UDATA_INDICATION_MODEM_CALLING_TONE  18
-#define DTMF_UDATA_INDICATION_FAX_CALLING_TONE    19
-#define DTMF_UDATA_INDICATION_ANSWER_TONE         20
-
-#define UDATA_REQUEST_MIXER_TAP_DATA        27
-#define UDATA_INDICATION_MIXER_TAP_DATA     27
-
-#define DTMF_LISTEN_ACTIVE_FLAG        0x01
-#define DTMF_SEND_DIGIT_FLAG           0x01
-
-
-/*------------------------------------------------------------------*/
-/* Mixer interface to IDI                                           */
-/*------------------------------------------------------------------*/
-
-
-#define LI2_FLAG_PCCONNECT_A_B 0x40000000
-#define LI2_FLAG_PCCONNECT_B_A 0x80000000
-
-#define MIXER_BCHANNELS_BRI    2
-#define MIXER_IC_CHANNELS_BRI  MIXER_BCHANNELS_BRI
-#define MIXER_IC_CHANNEL_BASE  MIXER_BCHANNELS_BRI
-#define MIXER_CHANNELS_BRI     (MIXER_BCHANNELS_BRI + MIXER_IC_CHANNELS_BRI)
-#define MIXER_CHANNELS_PRI     32
-
-typedef struct li_config_s LI_CONFIG;
-
-struct xconnect_card_address_s {
-       dword low;
-       dword high;
-};
-
-struct xconnect_transfer_address_s {
-       struct xconnect_card_address_s card_address;
-       dword offset;
-};
-
-struct li_config_s {
-       DIVA_CAPI_ADAPTER   *adapter;
-       PLCI   *plci;
-       struct xconnect_transfer_address_s send_b;
-       struct xconnect_transfer_address_s send_pc;
-       byte   *flag_table;  /* dword aligned and sized */
-       byte   *coef_table;  /* dword aligned and sized */
-       byte channel;
-       byte curchnl;
-       byte chflags;
-};
-
-extern LI_CONFIG   *li_config_table;
-extern word li_total_channels;
-
-#define LI_CHANNEL_INVOLVED        0x01
-#define LI_CHANNEL_ACTIVE          0x02
-#define LI_CHANNEL_TX_DATA         0x04
-#define LI_CHANNEL_RX_DATA         0x08
-#define LI_CHANNEL_CONFERENCE      0x10
-#define LI_CHANNEL_ADDRESSES_SET   0x80
-
-#define LI_CHFLAG_MONITOR          0x01
-#define LI_CHFLAG_MIX              0x02
-#define LI_CHFLAG_LOOP             0x04
-
-#define LI_FLAG_INTERCONNECT       0x01
-#define LI_FLAG_MONITOR            0x02
-#define LI_FLAG_MIX                0x04
-#define LI_FLAG_PCCONNECT          0x08
-#define LI_FLAG_CONFERENCE         0x10
-#define LI_FLAG_ANNOUNCEMENT       0x20
-
-#define LI_COEF_CH_CH              0x01
-#define LI_COEF_CH_PC              0x02
-#define LI_COEF_PC_CH              0x04
-#define LI_COEF_PC_PC              0x08
-#define LI_COEF_CH_CH_SET          0x10
-#define LI_COEF_CH_PC_SET          0x20
-#define LI_COEF_PC_CH_SET          0x40
-#define LI_COEF_PC_PC_SET          0x80
-
-#define LI_REQ_SILENT_UPDATE       0xffff
-
-#define LI_PLCI_B_LAST_FLAG        ((dword) 0x80000000L)
-#define LI_PLCI_B_DISC_FLAG        ((dword) 0x40000000L)
-#define LI_PLCI_B_SKIP_FLAG        ((dword) 0x20000000L)
-#define LI_PLCI_B_FLAG_MASK        ((dword) 0xe0000000L)
-
-#define UDATA_REQUEST_SET_MIXER_COEFS_BRI       24
-#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC  25
-#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_ASYN  26
-#define UDATA_INDICATION_MIXER_COEFS_SET        24
-
-#define MIXER_FEATURE_ENABLE_TX_DATA        0x0001
-#define MIXER_FEATURE_ENABLE_RX_DATA        0x0002
-
-#define MIXER_COEF_LINE_CHANNEL_MASK        0x1f
-#define MIXER_COEF_LINE_FROM_PC_FLAG        0x20
-#define MIXER_COEF_LINE_TO_PC_FLAG          0x40
-#define MIXER_COEF_LINE_ROW_FLAG            0x80
-
-#define UDATA_REQUEST_XCONNECT_FROM         28
-#define UDATA_INDICATION_XCONNECT_FROM      28
-#define UDATA_REQUEST_XCONNECT_TO           29
-#define UDATA_INDICATION_XCONNECT_TO        29
-
-#define XCONNECT_CHANNEL_PORT_B             0x0000
-#define XCONNECT_CHANNEL_PORT_PC            0x8000
-#define XCONNECT_CHANNEL_PORT_MASK          0x8000
-#define XCONNECT_CHANNEL_NUMBER_MASK        0x7fff
-#define XCONNECT_CHANNEL_PORT_COUNT         2
-
-#define XCONNECT_SUCCESS           0x0000
-#define XCONNECT_ERROR             0x0001
-
-
-/*------------------------------------------------------------------*/
-/* Echo canceller interface to IDI                                  */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_ECHO_CANCELLER         0
-
-#define PRIV_SELECTOR_ECHO_CANCELLER   255
-
-#define EC_ENABLE_OPERATION            1
-#define EC_DISABLE_OPERATION           2
-#define EC_FREEZE_COEFFICIENTS         3
-#define EC_RESUME_COEFFICIENT_UPDATE   4
-#define EC_RESET_COEFFICIENTS          5
-
-#define EC_DISABLE_NON_LINEAR_PROCESSING     0x0001
-#define EC_DO_NOT_REQUIRE_REVERSALS          0x0002
-#define EC_DETECT_DISABLE_TONE               0x0004
-
-#define EC_SUCCESS                           0
-#define EC_UNSUPPORTED_OPERATION             1
-
-#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ   1
-#define EC_BYPASS_DUE_TO_REVERSED_2100HZ     2
-#define EC_BYPASS_RELEASED                   3
-
-#define DSP_CTRL_SET_LEC_PARAMETERS          0x05
-
-#define LEC_ENABLE_ECHO_CANCELLER            0x0001
-#define LEC_ENABLE_2100HZ_DETECTOR           0x0002
-#define LEC_REQUIRE_2100HZ_REVERSALS         0x0004
-#define LEC_MANUAL_DISABLE                   0x0008
-#define LEC_ENABLE_NONLINEAR_PROCESSING      0x0010
-#define LEC_FREEZE_COEFFICIENTS              0x0020
-#define LEC_RESET_COEFFICIENTS               0x8000
-
-#define LEC_MAX_SUPPORTED_TAIL_LENGTH        32
-
-#define LEC_UDATA_INDICATION_DISABLE_DETECT  9
-
-#define LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ  0x00
-#define LEC_DISABLE_TYPE_REVERSED_2100HZ     0x01
-#define LEC_DISABLE_RELEASED                 0x02
-
-
-/*------------------------------------------------------------------*/
-/* RTP interface to IDI                                             */
-/*------------------------------------------------------------------*/
-
-
-#define B1_RTP                  31
-#define B2_RTP                  31
-#define B3_RTP                  31
-
-#define PRIVATE_RTP                    1
-
-#define RTP_PRIM_PAYLOAD_PCMU_8000     0
-#define RTP_PRIM_PAYLOAD_1016_8000     1
-#define RTP_PRIM_PAYLOAD_G726_32_8000  2
-#define RTP_PRIM_PAYLOAD_GSM_8000      3
-#define RTP_PRIM_PAYLOAD_G723_8000     4
-#define RTP_PRIM_PAYLOAD_DVI4_8000     5
-#define RTP_PRIM_PAYLOAD_DVI4_16000    6
-#define RTP_PRIM_PAYLOAD_LPC_8000      7
-#define RTP_PRIM_PAYLOAD_PCMA_8000     8
-#define RTP_PRIM_PAYLOAD_G722_16000    9
-#define RTP_PRIM_PAYLOAD_QCELP_8000    12
-#define RTP_PRIM_PAYLOAD_G728_8000     14
-#define RTP_PRIM_PAYLOAD_G729_8000     18
-#define RTP_PRIM_PAYLOAD_GSM_HR_8000   30
-#define RTP_PRIM_PAYLOAD_GSM_EFR_8000  31
-
-#define RTP_ADD_PAYLOAD_BASE           32
-#define RTP_ADD_PAYLOAD_RED            32
-#define RTP_ADD_PAYLOAD_CN_8000        33
-#define RTP_ADD_PAYLOAD_DTMF           34
-
-#define RTP_SUCCESS                         0
-#define RTP_ERR_SSRC_OR_PAYLOAD_CHANGE      1
-
-#define UDATA_REQUEST_RTP_RECONFIGURE       64
-#define UDATA_INDICATION_RTP_CHANGE         65
-#define BUDATA_REQUEST_QUERY_RTCP_REPORT    1
-#define BUDATA_INDICATION_RTCP_REPORT       1
-
-#define RTP_CONNECT_OPTION_DISC_ON_SSRC_CHANGE    0x00000001L
-#define RTP_CONNECT_OPTION_DISC_ON_PT_CHANGE      0x00000002L
-#define RTP_CONNECT_OPTION_DISC_ON_UNKNOWN_PT     0x00000004L
-#define RTP_CONNECT_OPTION_NO_SILENCE_TRANSMIT    0x00010000L
-
-#define RTP_PAYLOAD_OPTION_VOICE_ACTIVITY_DETECT  0x0001
-#define RTP_PAYLOAD_OPTION_DISABLE_POST_FILTER    0x0002
-#define RTP_PAYLOAD_OPTION_G723_LOW_CODING_RATE   0x0100
-
-#define RTP_PACKET_FILTER_IGNORE_UNKNOWN_SSRC     0x00000001L
-
-#define RTP_CHANGE_FLAG_SSRC_CHANGE               0x00000001L
-#define RTP_CHANGE_FLAG_PAYLOAD_TYPE_CHANGE       0x00000002L
-#define RTP_CHANGE_FLAG_UNKNOWN_PAYLOAD_TYPE      0x00000004L
-
-
-/*------------------------------------------------------------------*/
-/* T.38 interface to IDI                                            */
-/*------------------------------------------------------------------*/
-
-
-#define B1_T38                  30
-#define B2_T38                  30
-#define B3_T38                  30
-
-#define PRIVATE_T38                    2
-
-
-/*------------------------------------------------------------------*/
-/* PIAFS interface to IDI                                            */
-/*------------------------------------------------------------------*/
-
-
-#define B1_PIAFS                29
-#define B2_PIAFS                29
-
-#define PRIVATE_PIAFS           29
-
-/*
-  B2 configuration for PIAFS:
-  +---------------------+------+-----------------------------------------+
-  | PIAFS Protocol      | byte | Bit 1 - Protocol Speed                  |
-  | Speed configuration |      |         0 - 32K                         |
-  |                     |      |         1 - 64K (default)               |
-  |                     |      | Bit 2 - Variable Protocol Speed         |
-  |                     |      |         0 - Speed is fix                |
-  |                     |      |         1 - Speed is variable (default) |
-  +---------------------+------+-----------------------------------------+
-  | Direction           | word | Enable compression/decompression for    |
-  |                     |      | 0: All direction                        |
-  |                     |      | 1: disable outgoing data                |
-  |                     |      | 2: disable incoming data               |
-  |                     |      | 3: disable both direction (default)     |
-  +---------------------+------+-----------------------------------------+
-  | Number of code      | word | Parameter P1 of V.42bis in accordance   |
-  | words               |      | with V.42bis                            |
-  +---------------------+------+-----------------------------------------+
-  | Maximum String      | word | Parameter P2 of V.42bis in accordance   |
-  | Length              |      | with V.42bis                            |
-  +---------------------+------+-----------------------------------------+
-  | control (UDATA)     | byte | enable PIAFS control communication      |
-  | abilities           |      |                                         |
-  +---------------------+------+-----------------------------------------+
-*/
-#define PIAFS_UDATA_ABILITIES  0x80
-
-/*------------------------------------------------------------------*/
-/* FAX SUB/SEP/PWD extension                                        */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_FAX_SUB_SEP_PWD        3
-
-
-
-/*------------------------------------------------------------------*/
-/* V.18 extension                                                   */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_V18                    4
-
-
-
-/*------------------------------------------------------------------*/
-/* DTMF TONE extension                                              */
-/*------------------------------------------------------------------*/
-
-
-#define DTMF_GET_SUPPORTED_DETECT_CODES  0xf8
-#define DTMF_GET_SUPPORTED_SEND_CODES    0xf9
-#define DTMF_LISTEN_TONE_START           0xfa
-#define DTMF_LISTEN_TONE_STOP            0xfb
-#define DTMF_SEND_TONE                   0xfc
-#define DTMF_LISTEN_MF_START             0xfd
-#define DTMF_LISTEN_MF_STOP              0xfe
-#define DTMF_SEND_MF                     0xff
-
-#define DTMF_MF_DIGIT_TONE_CODE_1               0x10
-#define DTMF_MF_DIGIT_TONE_CODE_2               0x11
-#define DTMF_MF_DIGIT_TONE_CODE_3               0x12
-#define DTMF_MF_DIGIT_TONE_CODE_4               0x13
-#define DTMF_MF_DIGIT_TONE_CODE_5               0x14
-#define DTMF_MF_DIGIT_TONE_CODE_6               0x15
-#define DTMF_MF_DIGIT_TONE_CODE_7               0x16
-#define DTMF_MF_DIGIT_TONE_CODE_8               0x17
-#define DTMF_MF_DIGIT_TONE_CODE_9               0x18
-#define DTMF_MF_DIGIT_TONE_CODE_0               0x19
-#define DTMF_MF_DIGIT_TONE_CODE_K1              0x1a
-#define DTMF_MF_DIGIT_TONE_CODE_K2              0x1b
-#define DTMF_MF_DIGIT_TONE_CODE_KP              0x1c
-#define DTMF_MF_DIGIT_TONE_CODE_S1              0x1d
-#define DTMF_MF_DIGIT_TONE_CODE_ST              0x1e
-
-#define DTMF_DIGIT_CODE_COUNT                   16
-#define DTMF_MF_DIGIT_CODE_BASE                 DSP_DTMF_DIGIT_CODE_COUNT
-#define DTMF_MF_DIGIT_CODE_COUNT                15
-#define DTMF_TOTAL_DIGIT_CODE_COUNT             (DSP_MF_DIGIT_CODE_BASE + DSP_MF_DIGIT_CODE_COUNT)
-
-#define DTMF_TONE_DIGIT_BASE                    0x80
-
-#define DTMF_SIGNAL_NO_TONE                     (DTMF_TONE_DIGIT_BASE + 0)
-#define DTMF_SIGNAL_UNIDENTIFIED_TONE           (DTMF_TONE_DIGIT_BASE + 1)
-
-#define DTMF_SIGNAL_DIAL_TONE                   (DTMF_TONE_DIGIT_BASE + 2)
-#define DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE     (DTMF_TONE_DIGIT_BASE + 3)
-#define DTMF_SIGNAL_SPECIAL_DIAL_TONE           (DTMF_TONE_DIGIT_BASE + 4)   /* stutter dial tone */
-#define DTMF_SIGNAL_SECOND_DIAL_TONE            (DTMF_TONE_DIGIT_BASE + 5)
-#define DTMF_SIGNAL_RINGING_TONE                (DTMF_TONE_DIGIT_BASE + 6)
-#define DTMF_SIGNAL_SPECIAL_RINGING_TONE        (DTMF_TONE_DIGIT_BASE + 7)
-#define DTMF_SIGNAL_BUSY_TONE                   (DTMF_TONE_DIGIT_BASE + 8)
-#define DTMF_SIGNAL_CONGESTION_TONE             (DTMF_TONE_DIGIT_BASE + 9)   /* reorder tone */
-#define DTMF_SIGNAL_SPECIAL_INFORMATION_TONE    (DTMF_TONE_DIGIT_BASE + 10)
-#define DTMF_SIGNAL_COMFORT_TONE                (DTMF_TONE_DIGIT_BASE + 11)
-#define DTMF_SIGNAL_HOLD_TONE                   (DTMF_TONE_DIGIT_BASE + 12)
-#define DTMF_SIGNAL_RECORD_TONE                 (DTMF_TONE_DIGIT_BASE + 13)
-#define DTMF_SIGNAL_CALLER_WAITING_TONE         (DTMF_TONE_DIGIT_BASE + 14)
-#define DTMF_SIGNAL_CALL_WAITING_TONE           (DTMF_TONE_DIGIT_BASE + 15)
-#define DTMF_SIGNAL_PAY_TONE                    (DTMF_TONE_DIGIT_BASE + 16)
-#define DTMF_SIGNAL_POSITIVE_INDICATION_TONE    (DTMF_TONE_DIGIT_BASE + 17)
-#define DTMF_SIGNAL_NEGATIVE_INDICATION_TONE    (DTMF_TONE_DIGIT_BASE + 18)
-#define DTMF_SIGNAL_WARNING_TONE                (DTMF_TONE_DIGIT_BASE + 19)
-#define DTMF_SIGNAL_INTRUSION_TONE              (DTMF_TONE_DIGIT_BASE + 20)
-#define DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE   (DTMF_TONE_DIGIT_BASE + 21)
-#define DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE   (DTMF_TONE_DIGIT_BASE + 22)
-#define DTMF_SIGNAL_CPE_ALERTING_SIGNAL         (DTMF_TONE_DIGIT_BASE + 23)
-#define DTMF_SIGNAL_OFF_HOOK_WARNING_TONE       (DTMF_TONE_DIGIT_BASE + 24)
-
-#define DTMF_SIGNAL_INTERCEPT_TONE              (DTMF_TONE_DIGIT_BASE + 63)
-
-#define DTMF_SIGNAL_MODEM_CALLING_TONE          (DTMF_TONE_DIGIT_BASE + 64)
-#define DTMF_SIGNAL_FAX_CALLING_TONE            (DTMF_TONE_DIGIT_BASE + 65)
-#define DTMF_SIGNAL_ANSWER_TONE                 (DTMF_TONE_DIGIT_BASE + 66)
-#define DTMF_SIGNAL_REVERSED_ANSWER_TONE        (DTMF_TONE_DIGIT_BASE + 67)
-#define DTMF_SIGNAL_ANSAM_TONE                  (DTMF_TONE_DIGIT_BASE + 68)
-#define DTMF_SIGNAL_REVERSED_ANSAM_TONE         (DTMF_TONE_DIGIT_BASE + 69)
-#define DTMF_SIGNAL_BELL103_ANSWER_TONE         (DTMF_TONE_DIGIT_BASE + 70)
-#define DTMF_SIGNAL_FAX_FLAGS                   (DTMF_TONE_DIGIT_BASE + 71)
-#define DTMF_SIGNAL_G2_FAX_GROUP_ID             (DTMF_TONE_DIGIT_BASE + 72)
-#define DTMF_SIGNAL_HUMAN_SPEECH                (DTMF_TONE_DIGIT_BASE + 73)
-#define DTMF_SIGNAL_ANSWERING_MACHINE_390       (DTMF_TONE_DIGIT_BASE + 74)
-
-#define DTMF_MF_LISTEN_ACTIVE_FLAG     0x02
-#define DTMF_SEND_MF_FLAG              0x02
-#define DTMF_TONE_LISTEN_ACTIVE_FLAG   0x04
-#define DTMF_SEND_TONE_FLAG            0x04
-
-#define PRIVATE_DTMF_TONE              5
-
-
-/*------------------------------------------------------------------*/
-/* FAX paper format extension                                       */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_FAX_PAPER_FORMATS      6
-
-
-
-/*------------------------------------------------------------------*/
-/* V.OWN extension                                                  */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_VOWN                   7
-
-
-
-/*------------------------------------------------------------------*/
-/* FAX non-standard facilities extension                            */
-/*------------------------------------------------------------------*/
-
-
-#define PRIVATE_FAX_NONSTANDARD        8
-
-
-
-/*------------------------------------------------------------------*/
-/* Advanced voice                                                   */
-/*------------------------------------------------------------------*/
-
-#define ADV_VOICE_WRITE_ACTIVATION    0
-#define ADV_VOICE_WRITE_DEACTIVATION  1
-#define ADV_VOICE_WRITE_UPDATE        2
-
-#define ADV_VOICE_OLD_COEF_COUNT    6
-#define ADV_VOICE_NEW_COEF_BASE     (ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
-
-/*------------------------------------------------------------------*/
-/* B1 resource switching                                            */
-/*------------------------------------------------------------------*/
-
-#define B1_FACILITY_LOCAL  0x01
-#define B1_FACILITY_MIXER  0x02
-#define B1_FACILITY_DTMFX  0x04
-#define B1_FACILITY_DTMFR  0x08
-#define B1_FACILITY_VOICE  0x10
-#define B1_FACILITY_EC     0x20
-
-#define ADJUST_B_MODE_SAVE          0x0001
-#define ADJUST_B_MODE_REMOVE_L23    0x0002
-#define ADJUST_B_MODE_SWITCH_L1     0x0004
-#define ADJUST_B_MODE_NO_RESOURCE   0x0008
-#define ADJUST_B_MODE_ASSIGN_L23    0x0010
-#define ADJUST_B_MODE_USER_CONNECT  0x0020
-#define ADJUST_B_MODE_CONNECT       0x0040
-#define ADJUST_B_MODE_RESTORE       0x0080
-
-#define ADJUST_B_START                     0
-#define ADJUST_B_SAVE_MIXER_1              1
-#define ADJUST_B_SAVE_DTMF_1               2
-#define ADJUST_B_REMOVE_L23_1              3
-#define ADJUST_B_REMOVE_L23_2              4
-#define ADJUST_B_SAVE_EC_1                 5
-#define ADJUST_B_SAVE_DTMF_PARAMETER_1     6
-#define ADJUST_B_SAVE_VOICE_1              7
-#define ADJUST_B_SWITCH_L1_1               8
-#define ADJUST_B_SWITCH_L1_2               9
-#define ADJUST_B_RESTORE_VOICE_1           10
-#define ADJUST_B_RESTORE_VOICE_2           11
-#define ADJUST_B_RESTORE_DTMF_PARAMETER_1  12
-#define ADJUST_B_RESTORE_DTMF_PARAMETER_2  13
-#define ADJUST_B_RESTORE_EC_1              14
-#define ADJUST_B_RESTORE_EC_2              15
-#define ADJUST_B_ASSIGN_L23_1              16
-#define ADJUST_B_ASSIGN_L23_2              17
-#define ADJUST_B_CONNECT_1                 18
-#define ADJUST_B_CONNECT_2                 19
-#define ADJUST_B_CONNECT_3                 20
-#define ADJUST_B_CONNECT_4                 21
-#define ADJUST_B_RESTORE_DTMF_1            22
-#define ADJUST_B_RESTORE_DTMF_2            23
-#define ADJUST_B_RESTORE_MIXER_1           24
-#define ADJUST_B_RESTORE_MIXER_2           25
-#define ADJUST_B_RESTORE_MIXER_3           26
-#define ADJUST_B_RESTORE_MIXER_4           27
-#define ADJUST_B_RESTORE_MIXER_5           28
-#define ADJUST_B_RESTORE_MIXER_6           29
-#define ADJUST_B_RESTORE_MIXER_7           30
-#define ADJUST_B_END                       31
-
-/*------------------------------------------------------------------*/
-/* XON Protocol def's                                               */
-/*------------------------------------------------------------------*/
-#define N_CH_XOFF               0x01
-#define N_XON_SENT              0x02
-#define N_XON_REQ               0x04
-#define N_XON_CONNECT_IND       0x08
-#define N_RX_FLOW_CONTROL_MASK  0x3f
-#define N_OK_FC_PENDING         0x80
-#define N_TX_FLOW_CONTROL_MASK  0xc0
-
-/*------------------------------------------------------------------*/
-/* NCPI state                                                       */
-/*------------------------------------------------------------------*/
-#define NCPI_VALID_CONNECT_B3_IND  0x01
-#define NCPI_VALID_CONNECT_B3_ACT  0x02
-#define NCPI_VALID_DISC_B3_IND     0x04
-#define NCPI_CONNECT_B3_ACT_SENT   0x08
-#define NCPI_NEGOTIATE_B3_SENT     0x10
-#define NCPI_MDM_CTS_ON_RECEIVED   0x40
-#define NCPI_MDM_DCD_ON_RECEIVED   0x80
-
-/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/divamnt.c b/drivers/isdn/hardware/eicon/divamnt.c
deleted file mode 100644 (file)
index 5a95587..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/* $Id: divamnt.c,v 1.32.6.10 2005/02/11 19:40:25 armin Exp $
- *
- * Driver for Eicon DIVA Server ISDN cards.
- * Maint module
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-
-#include "platform.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "debug_if.h"
-
-static DEFINE_MUTEX(maint_mutex);
-static char *main_revision = "$Revision: 1.32.6.10 $";
-
-static int major;
-
-MODULE_DESCRIPTION("Maint driver for Eicon DIVA Server cards");
-MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
-MODULE_SUPPORTED_DEVICE("DIVA card driver");
-MODULE_LICENSE("GPL");
-
-static int buffer_length = 128;
-module_param(buffer_length, int, 0);
-static unsigned long diva_dbg_mem = 0;
-module_param(diva_dbg_mem, ulong, 0);
-
-static char *DRIVERNAME =
-       "Eicon DIVA - MAINT module (http://www.melware.net)";
-static char *DRIVERLNAME = "diva_mnt";
-static char *DEVNAME = "DivasMAINT";
-char *DRIVERRELEASE_MNT = "2.0";
-
-static wait_queue_head_t msgwaitq;
-static unsigned long opened;
-
-extern int mntfunc_init(int *, void **, unsigned long);
-extern void mntfunc_finit(void);
-extern int maint_read_write(void __user *buf, int count);
-
-/*
- *  helper functions
- */
-static char *getrev(const char *revision)
-{
-       char *rev;
-       char *p;
-
-       if ((p = strchr(revision, ':'))) {
-               rev = p + 2;
-               p = strchr(rev, '$');
-               *--p = 0;
-       } else
-               rev = "1.0";
-
-       return rev;
-}
-
-/*
- * kernel/user space copy functions
- */
-int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src,
-                        int length)
-{
-       return (copy_to_user(dst, src, length));
-}
-int diva_os_copy_from_user(void *os_handle, void *dst, const void __user *src,
-                          int length)
-{
-       return (copy_from_user(dst, src, length));
-}
-
-/*
- * get time
- */
-void diva_os_get_time(dword *sec, dword *usec)
-{
-       struct timespec64 time;
-
-       ktime_get_ts64(&time);
-
-       *sec = (dword) time.tv_sec;
-       *usec = (dword) (time.tv_nsec / NSEC_PER_USEC);
-}
-
-/*
- * device node operations
- */
-static __poll_t maint_poll(struct file *file, poll_table *wait)
-{
-       __poll_t mask = 0;
-
-       poll_wait(file, &msgwaitq, wait);
-       mask = EPOLLOUT | EPOLLWRNORM;
-       if (file->private_data || diva_dbg_q_length()) {
-               mask |= EPOLLIN | EPOLLRDNORM;
-       }
-       return (mask);
-}
-
-static int maint_open(struct inode *ino, struct file *filep)
-{
-       int ret;
-
-       mutex_lock(&maint_mutex);
-       /* only one open is allowed, so we test
-          it atomically */
-       if (test_and_set_bit(0, &opened))
-               ret = -EBUSY;
-       else {
-               filep->private_data = NULL;
-               ret = nonseekable_open(ino, filep);
-       }
-       mutex_unlock(&maint_mutex);
-       return ret;
-}
-
-static int maint_close(struct inode *ino, struct file *filep)
-{
-       if (filep->private_data) {
-               diva_os_free(0, filep->private_data);
-               filep->private_data = NULL;
-       }
-
-       /* clear 'used' flag */
-       clear_bit(0, &opened);
-
-       return (0);
-}
-
-static ssize_t divas_maint_write(struct file *file, const char __user *buf,
-                                size_t count, loff_t *ppos)
-{
-       return (maint_read_write((char __user *) buf, (int) count));
-}
-
-static ssize_t divas_maint_read(struct file *file, char __user *buf,
-                               size_t count, loff_t *ppos)
-{
-       return (maint_read_write(buf, (int) count));
-}
-
-static const struct file_operations divas_maint_fops = {
-       .owner   = THIS_MODULE,
-       .llseek  = no_llseek,
-       .read    = divas_maint_read,
-       .write   = divas_maint_write,
-       .poll    = maint_poll,
-       .open    = maint_open,
-       .release = maint_close
-};
-
-static void divas_maint_unregister_chrdev(void)
-{
-       unregister_chrdev(major, DEVNAME);
-}
-
-static int __init divas_maint_register_chrdev(void)
-{
-       if ((major = register_chrdev(0, DEVNAME, &divas_maint_fops)) < 0)
-       {
-               printk(KERN_ERR "%s: failed to create /dev entry.\n",
-                      DRIVERLNAME);
-               return (0);
-       }
-
-       return (1);
-}
-
-/*
- * wake up reader
- */
-void diva_maint_wakeup_read(void)
-{
-       wake_up_interruptible(&msgwaitq);
-}
-
-/*
- *  Driver Load
- */
-static int __init maint_init(void)
-{
-       char tmprev[50];
-       int ret = 0;
-       void *buffer = NULL;
-
-       init_waitqueue_head(&msgwaitq);
-
-       printk(KERN_INFO "%s\n", DRIVERNAME);
-       printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_MNT);
-       strcpy(tmprev, main_revision);
-       printk("%s  Build: %s \n", getrev(tmprev), DIVA_BUILD);
-
-       if (!divas_maint_register_chrdev()) {
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!(mntfunc_init(&buffer_length, &buffer, diva_dbg_mem))) {
-               printk(KERN_ERR "%s: failed to connect to DIDD.\n",
-                      DRIVERLNAME);
-               divas_maint_unregister_chrdev();
-               ret = -EIO;
-               goto out;
-       }
-
-       printk(KERN_INFO "%s: trace buffer = %p - %d kBytes, %s (Major: %d)\n",
-              DRIVERLNAME, buffer, (buffer_length / 1024),
-              (diva_dbg_mem == 0) ? "internal" : "external", major);
-
-out:
-       return (ret);
-}
-
-/*
-**  Driver Unload
-*/
-static void __exit maint_exit(void)
-{
-       divas_maint_unregister_chrdev();
-       mntfunc_finit();
-
-       printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
-}
-
-module_init(maint_init);
-module_exit(maint_exit);
diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c
deleted file mode 100644 (file)
index 4be5f88..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/* $Id: divasfunc.c,v 1.23.4.2 2004/08/28 20:03:53 armin Exp $
- *
- * Low level driver for Eicon DIVA Server ISDN cards.
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "di.h"
-#include "io.h"
-#include "divasync.h"
-#include "diva.h"
-#include "xdi_vers.h"
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-static int debugmask;
-
-extern void DIVA_DIDD_Read(void *, int);
-
-extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
-
-extern char *DRIVERRELEASE_DIVAS;
-
-static dword notify_handle;
-static DESCRIPTOR DAdapter;
-static DESCRIPTOR MAdapter;
-
-/* --------------------------------------------------------------------------
-   MAINT driver connector section
-   -------------------------------------------------------------------------- */
-static void no_printf(unsigned char *x, ...)
-{
-       /* dummy debug function */
-}
-
-#include "debuglib.c"
-
-/*
- * get the adapters serial number
- */
-void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf)
-{
-       int contr = 0;
-
-       if ((contr = ((IoAdapter->serialNo & 0xff000000) >> 24))) {
-               sprintf(buf, "%d-%d",
-                       IoAdapter->serialNo & 0x00ffffff, contr + 1);
-       } else {
-               sprintf(buf, "%d", IoAdapter->serialNo);
-       }
-}
-
-/*
- * register a new adapter
- */
-void diva_xdi_didd_register_adapter(int card)
-{
-       DESCRIPTOR d;
-       IDI_SYNC_REQ req;
-
-       if (card && ((card - 1) < MAX_ADAPTER) &&
-           IoAdapters[card - 1] && Requests[card - 1]) {
-               d.type = IoAdapters[card - 1]->Properties.DescType;
-               d.request = Requests[card - 1];
-               d.channels = IoAdapters[card - 1]->Properties.Channels;
-               d.features = IoAdapters[card - 1]->Properties.Features;
-               DBG_TRC(("DIDD register A(%d) channels=%d", card,
-                        d.channels))
-                       /* workaround for different Name in structure */
-                       strlcpy(IoAdapters[card - 1]->Name,
-                               IoAdapters[card - 1]->Properties.Name,
-                               sizeof(IoAdapters[card - 1]->Name));
-               req.didd_remove_adapter.e.Req = 0;
-               req.didd_add_adapter.e.Rc = IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
-               req.didd_add_adapter.info.descriptor = (void *) &d;
-               DAdapter.request((ENTITY *)&req);
-               if (req.didd_add_adapter.e.Rc != 0xff) {
-                       DBG_ERR(("DIDD register A(%d) failed !", card))
-                               }
-               IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
-       }
-}
-
-/*
- * remove an adapter
- */
-void diva_xdi_didd_remove_adapter(int card)
-{
-       IDI_SYNC_REQ req;
-       ADAPTER *a = &IoAdapters[card - 1]->a;
-
-       IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
-       DBG_TRC(("DIDD de-register A(%d)", card))
-               req.didd_remove_adapter.e.Req = 0;
-       req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
-       req.didd_remove_adapter.info.p_request =
-               (IDI_CALL) Requests[card - 1];
-       DAdapter.request((ENTITY *)&req);
-       memset(&(a->IdTable), 0x00, 256);
-}
-
-/*
- * start debug
- */
-static void start_dbg(void)
-{
-       DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT);
-       DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s])",
-                DIVA_BUILD, diva_xdi_common_code_build))
-               }
-
-/*
- * stop debug
- */
-static void stop_dbg(void)
-{
-       DbgDeregister();
-       memset(&MAdapter, 0, sizeof(MAdapter));
-       dprintf = no_printf;
-}
-
-/*
- * didd callback function
- */
-static void *didd_callback(void *context, DESCRIPTOR *adapter,
-                          int removal)
-{
-       if (adapter->type == IDI_DADAPTER) {
-               DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
-               return (NULL);
-       }
-
-       if (adapter->type == IDI_DIMAINT) {
-               if (removal) {
-                       stop_dbg();
-               } else {
-                       memcpy(&MAdapter, adapter, sizeof(MAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       start_dbg();
-               }
-       }
-       return (NULL);
-}
-
-/*
- * connect to didd
- */
-static int __init connect_didd(void)
-{
-       int x = 0;
-       int dadapter = 0;
-       IDI_SYNC_REQ req;
-       DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
-
-       DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
-
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
-                       dadapter = 1;
-                       memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       req.didd_notify.e.Req = 0;
-                       req.didd_notify.e.Rc =
-                               IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
-                       req.didd_notify.info.callback = (void *)didd_callback;
-                       req.didd_notify.info.context = NULL;
-                       DAdapter.request((ENTITY *)&req);
-                       if (req.didd_notify.e.Rc != 0xff) {
-                               stop_dbg();
-                               return (0);
-                       }
-                       notify_handle = req.didd_notify.info.handle;
-               } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
-                       memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       start_dbg();
-               }
-       }
-
-       if (!dadapter) {
-               stop_dbg();
-       }
-
-       return (dadapter);
-}
-
-/*
- * disconnect from didd
- */
-static void disconnect_didd(void)
-{
-       IDI_SYNC_REQ req;
-
-       stop_dbg();
-
-       req.didd_notify.e.Req = 0;
-       req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
-       req.didd_notify.info.handle = notify_handle;
-       DAdapter.request((ENTITY *)&req);
-}
-
-/*
- * init
- */
-int __init divasfunc_init(int dbgmask)
-{
-       char *version;
-
-       debugmask = dbgmask;
-
-       if (!connect_didd()) {
-               DBG_ERR(("divasfunc: failed to connect to DIDD."))
-                       return (0);
-       }
-
-       version = diva_xdi_common_code_build;
-
-       divasa_xdi_driver_entry();
-
-       return (1);
-}
-
-/*
- * exit
- */
-void divasfunc_exit(void)
-{
-       divasa_xdi_driver_unload();
-       disconnect_didd();
-}
diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c
deleted file mode 100644 (file)
index e7081e0..0000000
+++ /dev/null
@@ -1,562 +0,0 @@
-/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $
- *
- * Driver for Eicon DIVA Server ISDN cards.
- * User Mode IDI Interface
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-
-#include "platform.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "um_xdi.h"
-#include "um_idi.h"
-
-static char *main_revision = "$Revision: 1.25.6.2 $";
-
-static int major;
-
-MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards");
-MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
-MODULE_SUPPORTED_DEVICE("DIVA card driver");
-MODULE_LICENSE("GPL");
-
-typedef struct _diva_um_idi_os_context {
-       wait_queue_head_t read_wait;
-       wait_queue_head_t close_wait;
-       struct timer_list diva_timer_id;
-       int aborted;
-       int adapter_nr;
-} diva_um_idi_os_context_t;
-
-static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)";
-static char *DRIVERLNAME = "diva_idi";
-static char *DEVNAME = "DivasIDI";
-char *DRIVERRELEASE_IDI = "2.0";
-
-extern int idifunc_init(void);
-extern void idifunc_finit(void);
-
-/*
- *  helper functions
- */
-static char *getrev(const char *revision)
-{
-       char *rev;
-       char *p;
-       if ((p = strchr(revision, ':'))) {
-               rev = p + 2;
-               p = strchr(rev, '$');
-               *--p = 0;
-       } else
-               rev = "1.0";
-       return rev;
-}
-
-/*
- *  LOCALS
- */
-static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count,
-                          loff_t *offset);
-static ssize_t um_idi_write(struct file *file, const char __user *buf,
-                           size_t count, loff_t *offset);
-static __poll_t um_idi_poll(struct file *file, poll_table *wait);
-static int um_idi_open(struct inode *inode, struct file *file);
-static int um_idi_release(struct inode *inode, struct file *file);
-static int remove_entity(void *entity);
-static void diva_um_timer_function(struct timer_list *t);
-
-/*
- * proc entry
- */
-extern struct proc_dir_entry *proc_net_eicon;
-static struct proc_dir_entry *um_idi_proc_entry = NULL;
-
-static int um_idi_proc_show(struct seq_file *m, void *v)
-{
-       char tmprev[32];
-
-       seq_printf(m, "%s\n", DRIVERNAME);
-       seq_printf(m, "name     : %s\n", DRIVERLNAME);
-       seq_printf(m, "release  : %s\n", DRIVERRELEASE_IDI);
-       strcpy(tmprev, main_revision);
-       seq_printf(m, "revision : %s\n", getrev(tmprev));
-       seq_printf(m, "build    : %s\n", DIVA_BUILD);
-       seq_printf(m, "major    : %d\n", major);
-
-       return 0;
-}
-
-static int __init create_um_idi_proc(void)
-{
-       um_idi_proc_entry = proc_create_single(DRIVERLNAME, S_IRUGO,
-                       proc_net_eicon, um_idi_proc_show);
-       if (!um_idi_proc_entry)
-               return (0);
-       return (1);
-}
-
-static void remove_um_idi_proc(void)
-{
-       if (um_idi_proc_entry) {
-               remove_proc_entry(DRIVERLNAME, proc_net_eicon);
-               um_idi_proc_entry = NULL;
-       }
-}
-
-static const struct file_operations divas_idi_fops = {
-       .owner   = THIS_MODULE,
-       .llseek  = no_llseek,
-       .read    = um_idi_read,
-       .write   = um_idi_write,
-       .poll    = um_idi_poll,
-       .open    = um_idi_open,
-       .release = um_idi_release
-};
-
-static void divas_idi_unregister_chrdev(void)
-{
-       unregister_chrdev(major, DEVNAME);
-}
-
-static int __init divas_idi_register_chrdev(void)
-{
-       if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0)
-       {
-               printk(KERN_ERR "%s: failed to create /dev entry.\n",
-                      DRIVERLNAME);
-               return (0);
-       }
-
-       return (1);
-}
-
-/*
-** Driver Load
-*/
-static int __init divasi_init(void)
-{
-       char tmprev[50];
-       int ret = 0;
-
-       printk(KERN_INFO "%s\n", DRIVERNAME);
-       printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_IDI);
-       strcpy(tmprev, main_revision);
-       printk("%s  Build: %s\n", getrev(tmprev), DIVA_BUILD);
-
-       if (!divas_idi_register_chrdev()) {
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!create_um_idi_proc()) {
-               divas_idi_unregister_chrdev();
-               printk(KERN_ERR "%s: failed to create proc entry.\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!(idifunc_init())) {
-               remove_um_idi_proc();
-               divas_idi_unregister_chrdev();
-               printk(KERN_ERR "%s: failed to connect to DIDD.\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-               goto out;
-       }
-       printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
-
-out:
-       return (ret);
-}
-
-
-/*
-** Driver Unload
-*/
-static void __exit divasi_exit(void)
-{
-       idifunc_finit();
-       remove_um_idi_proc();
-       divas_idi_unregister_chrdev();
-
-       printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
-}
-
-module_init(divasi_init);
-module_exit(divasi_exit);
-
-
-/*
- *  FILE OPERATIONS
- */
-
-static int
-divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src,
-                         int length)
-{
-       memcpy(dst, src, length);
-       return (length);
-}
-
-static ssize_t
-um_idi_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
-{
-       diva_um_idi_os_context_t *p_os;
-       int ret = -EINVAL;
-       void *data;
-
-       if (!file->private_data) {
-               return (-ENODEV);
-       }
-
-       if (!
-           (p_os =
-            (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
-                                                                   private_data)))
-       {
-               return (-ENODEV);
-       }
-       if (p_os->aborted) {
-               return (-ENODEV);
-       }
-
-       if (!(data = diva_os_malloc(0, count))) {
-               return (-ENOMEM);
-       }
-
-       ret = diva_um_idi_read(file->private_data,
-                              file, data, count,
-                              divas_um_idi_copy_to_user);
-       switch (ret) {
-       case 0:         /* no message available */
-               ret = (-EAGAIN);
-               break;
-       case (-1):              /* adapter was removed */
-               ret = (-ENODEV);
-               break;
-       case (-2):              /* message_length > length of user buffer */
-               ret = (-EFAULT);
-               break;
-       }
-
-       if (ret > 0) {
-               if (copy_to_user(buf, data, ret)) {
-                       ret = (-EFAULT);
-               }
-       }
-
-       diva_os_free(0, data);
-       DBG_TRC(("read: ret %d", ret));
-       return (ret);
-}
-
-
-static int
-divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src,
-                           int length)
-{
-       memcpy(dst, src, length);
-       return (length);
-}
-
-static int um_idi_open_adapter(struct file *file, int adapter_nr)
-{
-       diva_um_idi_os_context_t *p_os;
-       void *e =
-               divas_um_idi_create_entity((dword) adapter_nr, (void *) file);
-
-       if (!(file->private_data = e)) {
-               return (0);
-       }
-       p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e);
-       init_waitqueue_head(&p_os->read_wait);
-       init_waitqueue_head(&p_os->close_wait);
-       timer_setup(&p_os->diva_timer_id, diva_um_timer_function, 0);
-       p_os->aborted = 0;
-       p_os->adapter_nr = adapter_nr;
-       return (1);
-}
-
-static ssize_t
-um_idi_write(struct file *file, const char __user *buf, size_t count,
-            loff_t *offset)
-{
-       diva_um_idi_os_context_t *p_os;
-       int ret = -EINVAL;
-       void *data;
-       int adapter_nr = 0;
-
-       if (!file->private_data) {
-               /* the first write() selects the adapter_nr */
-               if (count == sizeof(int)) {
-                       if (copy_from_user
-                           ((void *) &adapter_nr, buf,
-                            count)) return (-EFAULT);
-                       if (!(um_idi_open_adapter(file, adapter_nr)))
-                               return (-ENODEV);
-                       return (count);
-               } else
-                       return (-ENODEV);
-       }
-
-       if (!(p_os =
-             (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
-                                                                    private_data)))
-       {
-               return (-ENODEV);
-       }
-       if (p_os->aborted) {
-               return (-ENODEV);
-       }
-
-       if (!(data = diva_os_malloc(0, count))) {
-               return (-ENOMEM);
-       }
-
-       if (copy_from_user(data, buf, count)) {
-               ret = -EFAULT;
-       } else {
-               ret = diva_um_idi_write(file->private_data,
-                                       file, data, count,
-                                       divas_um_idi_copy_from_user);
-               switch (ret) {
-               case 0: /* no space available */
-                       ret = (-EAGAIN);
-                       break;
-               case (-1):      /* adapter was removed */
-                       ret = (-ENODEV);
-                       break;
-               case (-2):      /* length of user buffer > max message_length */
-                       ret = (-EFAULT);
-                       break;
-               }
-       }
-       diva_os_free(0, data);
-       DBG_TRC(("write: ret %d", ret));
-       return (ret);
-}
-
-static __poll_t um_idi_poll(struct file *file, poll_table *wait)
-{
-       diva_um_idi_os_context_t *p_os;
-
-       if (!file->private_data) {
-               return (EPOLLERR);
-       }
-
-       if ((!(p_os =
-              (diva_um_idi_os_context_t *)
-              diva_um_id_get_os_context(file->private_data)))
-           || p_os->aborted) {
-               return (EPOLLERR);
-       }
-
-       poll_wait(file, &p_os->read_wait, wait);
-
-       if (p_os->aborted) {
-               return (EPOLLERR);
-       }
-
-       switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {
-       case (-1):
-               return (EPOLLERR);
-
-       case 0:
-               return (0);
-       }
-
-       return (EPOLLIN | EPOLLRDNORM);
-}
-
-static int um_idi_open(struct inode *inode, struct file *file)
-{
-       return (0);
-}
-
-
-static int um_idi_release(struct inode *inode, struct file *file)
-{
-       diva_um_idi_os_context_t *p_os;
-       unsigned int adapter_nr;
-       int ret = 0;
-
-       if (!(file->private_data)) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       if (!(p_os =
-             (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       adapter_nr = p_os->adapter_nr;
-
-       if ((ret = remove_entity(file->private_data))) {
-               goto out;
-       }
-
-       if (divas_um_idi_delete_entity
-           ((int) adapter_nr, file->private_data)) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-out:
-       return (ret);
-}
-
-int diva_os_get_context_size(void)
-{
-       return (sizeof(diva_um_idi_os_context_t));
-}
-
-void diva_os_wakeup_read(void *os_context)
-{
-       diva_um_idi_os_context_t *p_os =
-               (diva_um_idi_os_context_t *) os_context;
-       wake_up_interruptible(&p_os->read_wait);
-}
-
-void diva_os_wakeup_close(void *os_context)
-{
-       diva_um_idi_os_context_t *p_os =
-               (diva_um_idi_os_context_t *) os_context;
-       wake_up_interruptible(&p_os->close_wait);
-}
-
-static
-void diva_um_timer_function(struct timer_list *t)
-{
-       diva_um_idi_os_context_t *p_os = from_timer(p_os, t, diva_timer_id);
-
-       p_os->aborted = 1;
-       wake_up_interruptible(&p_os->read_wait);
-       wake_up_interruptible(&p_os->close_wait);
-       DBG_ERR(("entity removal watchdog"))
-               }
-
-/*
-**  If application exits without entity removal this function will remove
-**  entity and block until removal is complete
-*/
-static int remove_entity(void *entity)
-{
-       struct task_struct *curtask = current;
-       diva_um_idi_os_context_t *p_os;
-
-       diva_um_idi_stop_wdog(entity);
-
-       if (!entity) {
-               DBG_FTL(("Zero entity on remove"))
-                       return (0);
-       }
-
-       if (!(p_os =
-             (diva_um_idi_os_context_t *)
-             diva_um_id_get_os_context(entity))) {
-               DBG_FTL(("Zero entity os context on remove"))
-                       return (0);
-       }
-
-       if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {
-               /*
-                 Entity is not assigned, also can be removed
-               */
-               return (0);
-       }
-
-       DBG_TRC(("E(%08x) check remove", entity))
-
-               /*
-                 If adapter not answers on remove request inside of
-                 10 Sec, then adapter is dead
-               */
-               diva_um_idi_start_wdog(entity);
-
-       {
-               DECLARE_WAITQUEUE(wait, curtask);
-
-               add_wait_queue(&p_os->close_wait, &wait);
-               for (;;) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       if (!divas_um_idi_entity_start_remove(entity)
-                           || p_os->aborted) {
-                               break;
-                       }
-                       schedule();
-               }
-               set_current_state(TASK_RUNNING);
-               remove_wait_queue(&p_os->close_wait, &wait);
-       }
-
-       DBG_TRC(("E(%08x) start remove", entity))
-       {
-               DECLARE_WAITQUEUE(wait, curtask);
-
-               add_wait_queue(&p_os->close_wait, &wait);
-               for (;;) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       if (!divas_um_idi_entity_assigned(entity)
-                           || p_os->aborted) {
-                               break;
-                       }
-                       schedule();
-               }
-               set_current_state(TASK_RUNNING);
-               remove_wait_queue(&p_os->close_wait, &wait);
-       }
-
-       DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,
-                p_os->aborted))
-
-               diva_um_idi_stop_wdog(entity);
-
-       p_os->aborted = 0;
-
-       return (0);
-}
-
-/*
- * timer watchdog
- */
-void diva_um_idi_start_wdog(void *entity)
-{
-       diva_um_idi_os_context_t *p_os;
-
-       if (entity &&
-           ((p_os =
-             (diva_um_idi_os_context_t *)
-             diva_um_id_get_os_context(entity)))) {
-               mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);
-       }
-}
-
-void diva_um_idi_stop_wdog(void *entity)
-{
-       diva_um_idi_os_context_t *p_os;
-
-       if (entity &&
-           ((p_os =
-             (diva_um_idi_os_context_t *)
-             diva_um_id_get_os_context(entity)))) {
-               del_timer(&p_os->diva_timer_id);
-       }
-}
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
deleted file mode 100644 (file)
index b6a3950..0000000
+++ /dev/null
@@ -1,848 +0,0 @@
-/* $Id: divasmain.c,v 1.55.4.6 2005/02/09 19:28:20 armin Exp $
- *
- * Low level driver for Eicon DIVA Server ISDN cards.
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/poll.h>
-#include <linux/kmod.h>
-
-#include "platform.h"
-#undef ID_MASK
-#undef N_DATA
-#include "pc.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "diva.h"
-#include "di.h"
-#include "io.h"
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "xdi_vers.h"
-#include "diva_dma.h"
-#include "diva_pci.h"
-
-static char *main_revision = "$Revision: 1.55.4.6 $";
-
-static int major;
-
-static int dbgmask;
-
-MODULE_DESCRIPTION("Kernel driver for Eicon DIVA Server cards");
-MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
-MODULE_LICENSE("GPL");
-
-module_param(dbgmask, int, 0);
-MODULE_PARM_DESC(dbgmask, "initial debug mask");
-
-static char *DRIVERNAME =
-       "Eicon DIVA Server driver (http://www.melware.net)";
-static char *DRIVERLNAME = "divas";
-static char *DEVNAME = "Divas";
-char *DRIVERRELEASE_DIVAS = "2.0";
-
-extern irqreturn_t diva_os_irq_wrapper(int irq, void *context);
-extern int create_divas_proc(void);
-extern void remove_divas_proc(void);
-extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
-extern int divasfunc_init(int dbgmask);
-extern void divasfunc_exit(void);
-
-typedef struct _diva_os_thread_dpc {
-       struct tasklet_struct divas_task;
-       diva_os_soft_isr_t *psoft_isr;
-} diva_os_thread_dpc_t;
-
-/* --------------------------------------------------------------------------
-   PCI driver interface section
-   -------------------------------------------------------------------------- */
-/*
-  vendor, device       Vendor and device ID to match (or PCI_ANY_ID)
-  subvendor,   Subsystem vendor and device ID to match (or PCI_ANY_ID)
-  subdevice
-  class,               Device class to match. The class_mask tells which bits
-  class_mask   of the class are honored during the comparison.
-  driver_data  Data private to the driver.
-*/
-
-#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2)
-#define PCI_DEVICE_ID_EICON_MAESTRAP_2       0xE015
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_4BRI_VOIP)
-#define PCI_DEVICE_ID_EICON_4BRI_VOIP        0xE016
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_4BRI_2_VOIP)
-#define PCI_DEVICE_ID_EICON_4BRI_2_VOIP      0xE017
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2)
-#define PCI_DEVICE_ID_EICON_BRI2M_2          0xE018
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP)
-#define PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP  0xE019
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_2F)
-#define PCI_DEVICE_ID_EICON_2F               0xE01A
-#endif
-
-#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2_VOIP)
-#define PCI_DEVICE_ID_EICON_BRI2M_2_VOIP     0xE01B
-#endif
-
-/*
-  This table should be sorted by PCI device ID
-*/
-static const struct pci_device_id divas_pci_tbl[] = {
-       /* Diva Server BRI-2M PCI 0xE010 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRA),
-         CARDTYPE_MAESTRA_PCI },
-       /* Diva Server 4BRI-8M PCI 0xE012 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAQ),
-         CARDTYPE_DIVASRV_Q_8M_PCI },
-       /* Diva Server 4BRI-8M 2.0 PCI 0xE013 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAQ_U),
-         CARDTYPE_DIVASRV_Q_8M_V2_PCI },
-       /* Diva Server PRI-30M PCI 0xE014 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP),
-         CARDTYPE_DIVASRV_P_30M_PCI },
-       /* Diva Server PRI 2.0 adapter 0xE015 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2),
-         CARDTYPE_DIVASRV_P_30M_V2_PCI },
-       /* Diva Server Voice 4BRI-8M PCI 0xE016 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_4BRI_VOIP),
-         CARDTYPE_DIVASRV_VOICE_Q_8M_PCI },
-       /* Diva Server Voice 4BRI-8M 2.0 PCI 0xE017 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_4BRI_2_VOIP),
-         CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI },
-       /* Diva Server BRI-2M 2.0 PCI 0xE018 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_BRI2M_2),
-         CARDTYPE_DIVASRV_B_2M_V2_PCI },
-       /* Diva Server Voice PRI 2.0 PCI 0xE019 */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP),
-         CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI },
-       /* Diva Server 2FX 0xE01A */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_2F),
-         CARDTYPE_DIVASRV_B_2F_PCI },
-       /* Diva Server Voice BRI-2M 2.0 PCI 0xE01B */
-       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_BRI2M_2_VOIP),
-         CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI },
-       { 0, }                  /* 0 terminated list. */
-};
-MODULE_DEVICE_TABLE(pci, divas_pci_tbl);
-
-static int divas_init_one(struct pci_dev *pdev,
-                         const struct pci_device_id *ent);
-static void divas_remove_one(struct pci_dev *pdev);
-
-static struct pci_driver diva_pci_driver = {
-       .name     = "divas",
-       .probe    = divas_init_one,
-       .remove   = divas_remove_one,
-       .id_table = divas_pci_tbl,
-};
-
-/*********************************************************
- ** little helper functions
- *********************************************************/
-static char *getrev(const char *revision)
-{
-       char *rev;
-       char *p;
-       if ((p = strchr(revision, ':'))) {
-               rev = p + 2;
-               p = strchr(rev, '$');
-               *--p = 0;
-       } else
-               rev = "1.0";
-       return rev;
-}
-
-void diva_log_info(unsigned char *format, ...)
-{
-       va_list args;
-       unsigned char line[160];
-
-       va_start(args, format);
-       vsnprintf(line, sizeof(line), format, args);
-       va_end(args);
-
-       printk(KERN_INFO "%s: %s\n", DRIVERLNAME, line);
-}
-
-void divas_get_version(char *p)
-{
-       char tmprev[32];
-
-       strcpy(tmprev, main_revision);
-       sprintf(p, "%s: %s(%s) %s(%s) major=%d\n", DRIVERLNAME, DRIVERRELEASE_DIVAS,
-               getrev(tmprev), diva_xdi_common_code_build, DIVA_BUILD, major);
-}
-
-/* --------------------------------------------------------------------------
-   PCI Bus services
-   -------------------------------------------------------------------------- */
-byte diva_os_get_pci_bus(void *pci_dev_handle)
-{
-       struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
-       return ((byte) pdev->bus->number);
-}
-
-byte diva_os_get_pci_func(void *pci_dev_handle)
-{
-       struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
-       return ((byte) pdev->devfn);
-}
-
-unsigned long divasa_get_pci_irq(unsigned char bus, unsigned char func,
-                                void *pci_dev_handle)
-{
-       unsigned char irq = 0;
-       struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
-
-       irq = dev->irq;
-
-       return ((unsigned long) irq);
-}
-
-unsigned long divasa_get_pci_bar(unsigned char bus, unsigned char func,
-                                int bar, void *pci_dev_handle)
-{
-       unsigned long ret = 0;
-       struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
-
-       if (bar < 6) {
-               ret = dev->resource[bar].start;
-       }
-
-       DBG_TRC(("GOT BAR[%d]=%08x", bar, ret));
-
-       {
-               unsigned long type = (ret & 0x00000001);
-               if (type & PCI_BASE_ADDRESS_SPACE_IO) {
-                       DBG_TRC(("  I/O"));
-                       ret &= PCI_BASE_ADDRESS_IO_MASK;
-               } else {
-                       DBG_TRC(("  memory"));
-                       ret &= PCI_BASE_ADDRESS_MEM_MASK;
-               }
-               DBG_TRC(("  final=%08x", ret));
-       }
-
-       return (ret);
-}
-
-void PCIwrite(byte bus, byte func, int offset, void *data, int length,
-             void *pci_dev_handle)
-{
-       struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
-
-       switch (length) {
-       case 1:         /* byte */
-               pci_write_config_byte(dev, offset,
-                                     *(unsigned char *) data);
-               break;
-       case 2:         /* word */
-               pci_write_config_word(dev, offset,
-                                     *(unsigned short *) data);
-               break;
-       case 4:         /* dword */
-               pci_write_config_dword(dev, offset,
-                                      *(unsigned int *) data);
-               break;
-
-       default:                /* buffer */
-               if (!(length % 4) && !(length & 0x03)) {        /* Copy as dword */
-                       dword *p = (dword *) data;
-                       length /= 4;
-
-                       while (length--) {
-                               pci_write_config_dword(dev, offset,
-                                                      *(unsigned int *)
-                                                      p++);
-                       }
-               } else {        /* copy as byte stream */
-                       byte *p = (byte *) data;
-
-                       while (length--) {
-                               pci_write_config_byte(dev, offset,
-                                                     *(unsigned char *)
-                                                     p++);
-                       }
-               }
-       }
-}
-
-void PCIread(byte bus, byte func, int offset, void *data, int length,
-            void *pci_dev_handle)
-{
-       struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
-
-       switch (length) {
-       case 1:         /* byte */
-               pci_read_config_byte(dev, offset, (unsigned char *) data);
-               break;
-       case 2:         /* word */
-               pci_read_config_word(dev, offset, (unsigned short *) data);
-               break;
-       case 4:         /* dword */
-               pci_read_config_dword(dev, offset, (unsigned int *) data);
-               break;
-
-       default:                /* buffer */
-               if (!(length % 4) && !(length & 0x03)) {        /* Copy as dword */
-                       dword *p = (dword *) data;
-                       length /= 4;
-
-                       while (length--) {
-                               pci_read_config_dword(dev, offset,
-                                                     (unsigned int *)
-                                                     p++);
-                       }
-               } else {        /* copy as byte stream */
-                       byte *p = (byte *) data;
-
-                       while (length--) {
-                               pci_read_config_byte(dev, offset,
-                                                    (unsigned char *)
-                                                    p++);
-                       }
-               }
-       }
-}
-
-/*
-  Init map with DMA pages. It is not problem if some allocations fail -
-  the channels that will not get one DMA page will use standard PIO
-  interface
-*/
-static void *diva_pci_alloc_consistent(struct pci_dev *hwdev,
-                                      size_t size,
-                                      dma_addr_t *dma_handle,
-                                      void **addr_handle)
-{
-       void *addr = pci_alloc_consistent(hwdev, size, dma_handle);
-
-       *addr_handle = addr;
-
-       return (addr);
-}
-
-void diva_init_dma_map(void *hdev,
-                      struct _diva_dma_map_entry **ppmap, int nentries)
-{
-       struct pci_dev *pdev = (struct pci_dev *) hdev;
-       struct _diva_dma_map_entry *pmap =
-               diva_alloc_dma_map(hdev, nentries);
-
-       if (pmap) {
-               int i;
-               dma_addr_t dma_handle;
-               void *cpu_addr;
-               void *addr_handle;
-
-               for (i = 0; i < nentries; i++) {
-                       if (!(cpu_addr = diva_pci_alloc_consistent(pdev,
-                                                                  PAGE_SIZE,
-                                                                  &dma_handle,
-                                                                  &addr_handle)))
-                       {
-                               break;
-                       }
-                       diva_init_dma_map_entry(pmap, i, cpu_addr,
-                                               (dword) dma_handle,
-                                               addr_handle);
-                       DBG_TRC(("dma map alloc [%d]=(%08lx:%08x:%08lx)",
-                                i, (unsigned long) cpu_addr,
-                                (dword) dma_handle,
-                                (unsigned long) addr_handle))}
-       }
-
-       *ppmap = pmap;
-}
-
-/*
-  Free all contained in the map entries and memory used by the map
-  Should be always called after adapter removal from DIDD array
-*/
-void diva_free_dma_map(void *hdev, struct _diva_dma_map_entry *pmap)
-{
-       struct pci_dev *pdev = (struct pci_dev *) hdev;
-       int i;
-       dword phys_addr;
-       void *cpu_addr;
-       dma_addr_t dma_handle;
-       void *addr_handle;
-
-       for (i = 0; (pmap != NULL); i++) {
-               diva_get_dma_map_entry(pmap, i, &cpu_addr, &phys_addr);
-               if (!cpu_addr) {
-                       break;
-               }
-               addr_handle = diva_get_entry_handle(pmap, i);
-               dma_handle = (dma_addr_t) phys_addr;
-               pci_free_consistent(pdev, PAGE_SIZE, addr_handle,
-                                   dma_handle);
-               DBG_TRC(("dma map free [%d]=(%08lx:%08x:%08lx)", i,
-                        (unsigned long) cpu_addr, (dword) dma_handle,
-                        (unsigned long) addr_handle))
-                       }
-
-       diva_free_dma_mapping(pmap);
-}
-
-
-/*********************************************************
- ** I/O port utilities
- *********************************************************/
-
-int
-diva_os_register_io_port(void *adapter, int on, unsigned long port,
-                        unsigned long length, const char *name, int id)
-{
-       if (on) {
-               if (!request_region(port, length, name)) {
-                       DBG_ERR(("A: I/O: can't register port=%08x", port))
-                               return (-1);
-               }
-       } else {
-               release_region(port, length);
-       }
-       return (0);
-}
-
-void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, int id, unsigned long bar, unsigned long area_length)
-{
-       void __iomem *ret = ioremap(bar, area_length);
-       DBG_TRC(("remap(%08x)->%p", bar, ret));
-       return (ret);
-}
-
-void divasa_unmap_pci_bar(void __iomem *bar)
-{
-       if (bar) {
-               iounmap(bar);
-       }
-}
-
-/*********************************************************
- ** I/O port access
- *********************************************************/
-inline byte inpp(void __iomem *addr)
-{
-       return (inb((unsigned long) addr));
-}
-
-inline word inppw(void __iomem *addr)
-{
-       return (inw((unsigned long) addr));
-}
-
-inline void inppw_buffer(void __iomem *addr, void *P, int length)
-{
-       insw((unsigned long) addr, (word *) P, length >> 1);
-}
-
-inline void outppw_buffer(void __iomem *addr, void *P, int length)
-{
-       outsw((unsigned long) addr, (word *) P, length >> 1);
-}
-
-inline void outppw(void __iomem *addr, word w)
-{
-       outw(w, (unsigned long) addr);
-}
-
-inline void outpp(void __iomem *addr, word p)
-{
-       outb(p, (unsigned long) addr);
-}
-
-/* --------------------------------------------------------------------------
-   IRQ request / remove
-   -------------------------------------------------------------------------- */
-int diva_os_register_irq(void *context, byte irq, const char *name)
-{
-       int result = request_irq(irq, diva_os_irq_wrapper,
-                                IRQF_SHARED, name, context);
-       return (result);
-}
-
-void diva_os_remove_irq(void *context, byte irq)
-{
-       free_irq(irq, context);
-}
-
-/* --------------------------------------------------------------------------
-   DPC framework implementation
-   -------------------------------------------------------------------------- */
-static void diva_os_dpc_proc(unsigned long context)
-{
-       diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context;
-       diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr;
-
-       (*(pisr->callback)) (pisr, pisr->callback_context);
-}
-
-int diva_os_initialize_soft_isr(diva_os_soft_isr_t *psoft_isr,
-                               diva_os_soft_isr_callback_t callback,
-                               void *callback_context)
-{
-       diva_os_thread_dpc_t *pdpc;
-
-       pdpc = (diva_os_thread_dpc_t *) diva_os_malloc(0, sizeof(*pdpc));
-       if (!(psoft_isr->object = pdpc)) {
-               return (-1);
-       }
-       memset(pdpc, 0x00, sizeof(*pdpc));
-       psoft_isr->callback = callback;
-       psoft_isr->callback_context = callback_context;
-       pdpc->psoft_isr = psoft_isr;
-       tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc);
-
-       return (0);
-}
-
-int diva_os_schedule_soft_isr(diva_os_soft_isr_t *psoft_isr)
-{
-       if (psoft_isr && psoft_isr->object) {
-               diva_os_thread_dpc_t *pdpc =
-                       (diva_os_thread_dpc_t *) psoft_isr->object;
-
-               tasklet_schedule(&pdpc->divas_task);
-       }
-
-       return (1);
-}
-
-int diva_os_cancel_soft_isr(diva_os_soft_isr_t *psoft_isr)
-{
-       return (0);
-}
-
-void diva_os_remove_soft_isr(diva_os_soft_isr_t *psoft_isr)
-{
-       if (psoft_isr && psoft_isr->object) {
-               diva_os_thread_dpc_t *pdpc =
-                       (diva_os_thread_dpc_t *) psoft_isr->object;
-               void *mem;
-
-               tasklet_kill(&pdpc->divas_task);
-               mem = psoft_isr->object;
-               psoft_isr->object = NULL;
-               diva_os_free(0, mem);
-       }
-}
-
-/*
- * kernel/user space copy functions
- */
-static int
-xdi_copy_to_user(void *os_handle, void __user *dst, const void *src, int length)
-{
-       if (copy_to_user(dst, src, length)) {
-               return (-EFAULT);
-       }
-       return (length);
-}
-
-static int
-xdi_copy_from_user(void *os_handle, void *dst, const void __user *src, int length)
-{
-       if (copy_from_user(dst, src, length)) {
-               return (-EFAULT);
-       }
-       return (length);
-}
-
-/*
- * device node operations
- */
-static int divas_open(struct inode *inode, struct file *file)
-{
-       return (0);
-}
-
-static int divas_release(struct inode *inode, struct file *file)
-{
-       if (file->private_data) {
-               diva_xdi_close_adapter(file->private_data, file);
-       }
-       return (0);
-}
-
-static ssize_t divas_write(struct file *file, const char __user *buf,
-                          size_t count, loff_t *ppos)
-{
-       diva_xdi_um_cfg_cmd_t msg;
-       int ret = -EINVAL;
-
-       if (!file->private_data) {
-               file->private_data = diva_xdi_open_adapter(file, buf,
-                                                          count, &msg,
-                                                          xdi_copy_from_user);
-               if (!file->private_data)
-                       return (-ENODEV);
-               ret = diva_xdi_write(file->private_data, file,
-                                    buf, count, &msg, xdi_copy_from_user);
-       } else {
-               ret = diva_xdi_write(file->private_data, file,
-                                    buf, count, NULL, xdi_copy_from_user);
-       }
-
-       switch (ret) {
-       case -1:                /* Message should be removed from rx mailbox first */
-               ret = -EBUSY;
-               break;
-       case -2:                /* invalid adapter was specified in this call */
-               ret = -ENOMEM;
-               break;
-       case -3:
-               ret = -ENXIO;
-               break;
-       }
-       DBG_TRC(("write: ret %d", ret));
-       return (ret);
-}
-
-static ssize_t divas_read(struct file *file, char __user *buf,
-                         size_t count, loff_t *ppos)
-{
-       diva_xdi_um_cfg_cmd_t msg;
-       int ret = -EINVAL;
-
-       if (!file->private_data) {
-               file->private_data = diva_xdi_open_adapter(file, buf,
-                                                          count, &msg,
-                                                          xdi_copy_from_user);
-       }
-       if (!file->private_data) {
-               return (-ENODEV);
-       }
-
-       ret = diva_xdi_read(file->private_data, file,
-                           buf, count, xdi_copy_to_user);
-       switch (ret) {
-       case -1:                /* RX mailbox is empty */
-               ret = -EAGAIN;
-               break;
-       case -2:                /* no memory, mailbox was cleared, last command is failed */
-               ret = -ENOMEM;
-               break;
-       case -3:                /* can't copy to user, retry */
-               ret = -EFAULT;
-               break;
-       }
-       DBG_TRC(("read: ret %d", ret));
-       return (ret);
-}
-
-static __poll_t divas_poll(struct file *file, poll_table *wait)
-{
-       if (!file->private_data) {
-               return (EPOLLERR);
-       }
-       return (EPOLLIN | EPOLLRDNORM);
-}
-
-static const struct file_operations divas_fops = {
-       .owner   = THIS_MODULE,
-       .llseek  = no_llseek,
-       .read    = divas_read,
-       .write   = divas_write,
-       .poll    = divas_poll,
-       .open    = divas_open,
-       .release = divas_release
-};
-
-static void divas_unregister_chrdev(void)
-{
-       unregister_chrdev(major, DEVNAME);
-}
-
-static int __init divas_register_chrdev(void)
-{
-       if ((major = register_chrdev(0, DEVNAME, &divas_fops)) < 0)
-       {
-               printk(KERN_ERR "%s: failed to create /dev entry.\n",
-                      DRIVERLNAME);
-               return (0);
-       }
-
-       return (1);
-}
-
-/* --------------------------------------------------------------------------
-   PCI driver section
-   -------------------------------------------------------------------------- */
-static int divas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
-       void *pdiva = NULL;
-       u8 pci_latency;
-       u8 new_latency = 32;
-
-       DBG_TRC(("%s bus: %08x fn: %08x insertion.\n",
-                CardProperties[ent->driver_data].Name,
-                pdev->bus->number, pdev->devfn))
-               printk(KERN_INFO "%s: %s bus: %08x fn: %08x insertion.\n",
-                      DRIVERLNAME, CardProperties[ent->driver_data].Name,
-                      pdev->bus->number, pdev->devfn);
-
-       if (pci_enable_device(pdev)) {
-               DBG_TRC(("%s: %s bus: %08x fn: %08x device init failed.\n",
-                        DRIVERLNAME,
-                        CardProperties[ent->driver_data].Name,
-                        pdev->bus->number,
-                        pdev->devfn))
-                       printk(KERN_ERR
-                              "%s: %s bus: %08x fn: %08x device init failed.\n",
-                              DRIVERLNAME,
-                              CardProperties[ent->driver_data].
-                              Name, pdev->bus->number,
-                              pdev->devfn);
-               return (-EIO);
-       }
-
-       pci_set_master(pdev);
-
-       pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
-       if (!pci_latency) {
-               DBG_TRC(("%s: bus: %08x fn: %08x fix latency.\n",
-                        DRIVERLNAME, pdev->bus->number, pdev->devfn))
-                       printk(KERN_INFO
-                              "%s: bus: %08x fn: %08x fix latency.\n",
-                              DRIVERLNAME, pdev->bus->number, pdev->devfn);
-               pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency);
-       }
-
-       if (!(pdiva = diva_driver_add_card(pdev, ent->driver_data))) {
-               DBG_TRC(("%s: %s bus: %08x fn: %08x card init failed.\n",
-                        DRIVERLNAME,
-                        CardProperties[ent->driver_data].Name,
-                        pdev->bus->number,
-                        pdev->devfn))
-                       printk(KERN_ERR
-                              "%s: %s bus: %08x fn: %08x card init failed.\n",
-                              DRIVERLNAME,
-                              CardProperties[ent->driver_data].
-                              Name, pdev->bus->number,
-                              pdev->devfn);
-               return (-EIO);
-       }
-
-       pci_set_drvdata(pdev, pdiva);
-
-       return (0);
-}
-
-static void divas_remove_one(struct pci_dev *pdev)
-{
-       void *pdiva = pci_get_drvdata(pdev);
-
-       DBG_TRC(("bus: %08x fn: %08x removal.\n",
-                pdev->bus->number, pdev->devfn))
-               printk(KERN_INFO "%s: bus: %08x fn: %08x removal.\n",
-                      DRIVERLNAME, pdev->bus->number, pdev->devfn);
-
-       if (pdiva) {
-               diva_driver_remove_card(pdiva);
-       }
-
-}
-
-/* --------------------------------------------------------------------------
-   Driver Load / Startup
-   -------------------------------------------------------------------------- */
-static int __init divas_init(void)
-{
-       char tmprev[50];
-       int ret = 0;
-
-       printk(KERN_INFO "%s\n", DRIVERNAME);
-       printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_DIVAS);
-       strcpy(tmprev, main_revision);
-       printk("%s  Build: %s(%s)\n", getrev(tmprev),
-              diva_xdi_common_code_build, DIVA_BUILD);
-       printk(KERN_INFO "%s: support for: ", DRIVERLNAME);
-#ifdef CONFIG_ISDN_DIVAS_BRIPCI
-       printk("BRI/PCI ");
-#endif
-#ifdef CONFIG_ISDN_DIVAS_PRIPCI
-       printk("PRI/PCI ");
-#endif
-       printk("adapters\n");
-
-       if (!divasfunc_init(dbgmask)) {
-               printk(KERN_ERR "%s: failed to connect to DIDD.\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!divas_register_chrdev()) {
-#ifdef MODULE
-               divasfunc_exit();
-#endif
-               ret = -EIO;
-               goto out;
-       }
-
-       if (!create_divas_proc()) {
-#ifdef MODULE
-               divas_unregister_chrdev();
-               divasfunc_exit();
-#endif
-               printk(KERN_ERR "%s: failed to create proc entry.\n",
-                      DRIVERLNAME);
-               ret = -EIO;
-               goto out;
-       }
-
-       if ((ret = pci_register_driver(&diva_pci_driver))) {
-#ifdef MODULE
-               remove_divas_proc();
-               divas_unregister_chrdev();
-               divasfunc_exit();
-#endif
-               printk(KERN_ERR "%s: failed to init pci driver.\n",
-                      DRIVERLNAME);
-               goto out;
-       }
-       printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
-
-out:
-       return (ret);
-}
-
-/* --------------------------------------------------------------------------
-   Driver Unload
-   -------------------------------------------------------------------------- */
-static void __exit divas_exit(void)
-{
-       pci_unregister_driver(&diva_pci_driver);
-       remove_divas_proc();
-       divas_unregister_chrdev();
-       divasfunc_exit();
-
-       printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
-}
-
-module_init(divas_init);
-module_exit(divas_exit);
diff --git a/drivers/isdn/hardware/eicon/divasproc.c b/drivers/isdn/hardware/eicon/divasproc.c
deleted file mode 100644 (file)
index f52f462..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-/* $Id: divasproc.c,v 1.19.4.3 2005/01/31 12:22:20 armin Exp $
- *
- * Low level driver for Eicon DIVA Server ISDN cards.
- * /proc functions
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/list.h>
-#include <linux/uaccess.h>
-
-#include "platform.h"
-#include "debuglib.h"
-#undef ID_MASK
-#undef N_DATA
-#include "pc.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "di.h"
-#include "io.h"
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "diva.h"
-#include "diva_pci.h"
-
-
-extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
-extern void divas_get_version(char *);
-extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
-
-/*********************************************************
- ** Functions for /proc interface / File operations
- *********************************************************/
-
-static char *divas_proc_name = "divas";
-static char *adapter_dir_name = "adapter";
-static char *info_proc_name = "info";
-static char *grp_opt_proc_name = "group_optimization";
-static char *d_l1_down_proc_name = "dynamic_l1_down";
-
-/*
-** "divas" entry
-*/
-
-extern struct proc_dir_entry *proc_net_eicon;
-static struct proc_dir_entry *divas_proc_entry = NULL;
-
-static ssize_t
-divas_read(struct file *file, char __user *buf, size_t count, loff_t *off)
-{
-       int len = 0;
-       int cadapter;
-       char tmpbuf[80];
-       char tmpser[16];
-
-       if (*off)
-               return 0;
-
-       divas_get_version(tmpbuf);
-       if (copy_to_user(buf + len, &tmpbuf, strlen(tmpbuf)))
-               return -EFAULT;
-       len += strlen(tmpbuf);
-
-       for (cadapter = 0; cadapter < MAX_ADAPTER; cadapter++) {
-               if (IoAdapters[cadapter]) {
-                       diva_get_vserial_number(IoAdapters[cadapter],
-                                               tmpser);
-                       sprintf(tmpbuf,
-                               "%2d: %-30s Serial:%-10s IRQ:%2d\n",
-                               cadapter + 1,
-                               IoAdapters[cadapter]->Properties.Name,
-                               tmpser,
-                               IoAdapters[cadapter]->irq_info.irq_nr);
-                       if ((strlen(tmpbuf) + len) > count)
-                               break;
-                       if (copy_to_user
-                           (buf + len, &tmpbuf,
-                            strlen(tmpbuf))) return -EFAULT;
-                       len += strlen(tmpbuf);
-               }
-       }
-
-       *off += len;
-       return (len);
-}
-
-static ssize_t
-divas_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
-{
-       return (-ENODEV);
-}
-
-static __poll_t divas_poll(struct file *file, poll_table *wait)
-{
-       return (EPOLLERR);
-}
-
-static int divas_open(struct inode *inode, struct file *file)
-{
-       return nonseekable_open(inode, file);
-}
-
-static int divas_close(struct inode *inode, struct file *file)
-{
-       return (0);
-}
-
-static const struct file_operations divas_fops = {
-       .owner   = THIS_MODULE,
-       .llseek  = no_llseek,
-       .read    = divas_read,
-       .write   = divas_write,
-       .poll    = divas_poll,
-       .open    = divas_open,
-       .release = divas_close
-};
-
-int create_divas_proc(void)
-{
-       divas_proc_entry = proc_create(divas_proc_name, S_IFREG | S_IRUGO,
-                                      proc_net_eicon, &divas_fops);
-       if (!divas_proc_entry)
-               return (0);
-
-       return (1);
-}
-
-void remove_divas_proc(void)
-{
-       if (divas_proc_entry) {
-               remove_proc_entry(divas_proc_name, proc_net_eicon);
-               divas_proc_entry = NULL;
-       }
-}
-
-static ssize_t grp_opt_proc_write(struct file *file, const char __user *buffer,
-                                 size_t count, loff_t *pos)
-{
-       diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file));
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-
-       if ((count == 1) || (count == 2)) {
-               char c;
-               if (get_user(c, buffer))
-                       return -EFAULT;
-               switch (c) {
-               case '0':
-                       IoAdapter->capi_cfg.cfg_1 &=
-                               ~DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
-                       break;
-               case '1':
-                       IoAdapter->capi_cfg.cfg_1 |=
-                               DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
-                       break;
-               default:
-                       return (-EINVAL);
-               }
-               return (count);
-       }
-       return (-EINVAL);
-}
-
-static ssize_t d_l1_down_proc_write(struct file *file, const char __user *buffer,
-                                   size_t count, loff_t *pos)
-{
-       diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file));
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-
-       if ((count == 1) || (count == 2)) {
-               char c;
-               if (get_user(c, buffer))
-                       return -EFAULT;
-               switch (c) {
-               case '0':
-                       IoAdapter->capi_cfg.cfg_1 &=
-                               ~DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
-                       break;
-               case '1':
-                       IoAdapter->capi_cfg.cfg_1 |=
-                               DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
-                       break;
-               default:
-                       return (-EINVAL);
-               }
-               return (count);
-       }
-       return (-EINVAL);
-}
-
-static int d_l1_down_proc_show(struct seq_file *m, void *v)
-{
-       diva_os_xdi_adapter_t *a = m->private;
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-
-       seq_printf(m, "%s\n",
-                  (IoAdapter->capi_cfg.
-                   cfg_1 & DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? "1" :
-                  "0");
-       return 0;
-}
-
-static int d_l1_down_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, d_l1_down_proc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations d_l1_down_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = d_l1_down_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = d_l1_down_proc_write,
-};
-
-static int grp_opt_proc_show(struct seq_file *m, void *v)
-{
-       diva_os_xdi_adapter_t *a = m->private;
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-
-       seq_printf(m, "%s\n",
-                  (IoAdapter->capi_cfg.
-                   cfg_1 & DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON)
-                  ? "1" : "0");
-       return 0;
-}
-
-static int grp_opt_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, grp_opt_proc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations grp_opt_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = grp_opt_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = grp_opt_proc_write,
-};
-
-static ssize_t info_proc_write(struct file *file, const char __user *buffer,
-                              size_t count, loff_t *pos)
-{
-       diva_os_xdi_adapter_t *a = PDE_DATA(file_inode(file));
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-       char c[4];
-
-       if (count <= 4)
-               return -EINVAL;
-
-       if (copy_from_user(c, buffer, 4))
-               return -EFAULT;
-
-       /* this is for test purposes only */
-       if (!memcmp(c, "trap", 4)) {
-               (*(IoAdapter->os_trap_nfy_Fnc)) (IoAdapter, IoAdapter->ANum);
-               return (count);
-       }
-       return (-EINVAL);
-}
-
-static int info_proc_show(struct seq_file *m, void *v)
-{
-       int i = 0;
-       char *p;
-       char tmpser[16];
-       diva_os_xdi_adapter_t *a = m->private;
-       PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
-
-       seq_printf(m, "Name        : %s\n", IoAdapter->Properties.Name);
-       seq_printf(m, "DSP state   : %08x\n", a->dsp_mask);
-       seq_printf(m, "Channels    : %02d\n", IoAdapter->Properties.Channels);
-       seq_printf(m, "E. max/used : %03d/%03d\n",
-                  IoAdapter->e_max, IoAdapter->e_count);
-       diva_get_vserial_number(IoAdapter, tmpser);
-       seq_printf(m, "Serial      : %s\n", tmpser);
-       seq_printf(m, "IRQ         : %d\n", IoAdapter->irq_info.irq_nr);
-       seq_printf(m, "CardIndex   : %d\n", a->CardIndex);
-       seq_printf(m, "CardOrdinal : %d\n", a->CardOrdinal);
-       seq_printf(m, "Controller  : %d\n", a->controller);
-       seq_printf(m, "Bus-Type    : %s\n",
-                  (a->Bus ==
-                   DIVAS_XDI_ADAPTER_BUS_ISA) ? "ISA" : "PCI");
-       seq_printf(m, "Port-Name   : %s\n", a->port_name);
-       if (a->Bus == DIVAS_XDI_ADAPTER_BUS_PCI) {
-               seq_printf(m, "PCI-bus     : %d\n", a->resources.pci.bus);
-               seq_printf(m, "PCI-func    : %d\n", a->resources.pci.func);
-               for (i = 0; i < 8; i++) {
-                       if (a->resources.pci.bar[i]) {
-                               seq_printf(m,
-                                          "Mem / I/O %d : 0x%x / mapped : 0x%lx",
-                                          i, a->resources.pci.bar[i],
-                                          (unsigned long) a->resources.
-                                          pci.addr[i]);
-                               if (a->resources.pci.length[i]) {
-                                       seq_printf(m,
-                                                  " / length : %d",
-                                                  a->resources.pci.
-                                                  length[i]);
-                               }
-                               seq_putc(m, '\n');
-                       }
-               }
-       }
-       if ((!a->xdi_adapter.port) &&
-           ((!a->xdi_adapter.ram) ||
-            (!a->xdi_adapter.reset)
-            || (!a->xdi_adapter.cfg))) {
-               if (!IoAdapter->irq_info.irq_nr) {
-                       p = "slave";
-               } else {
-                       p = "out of service";
-               }
-       } else if (a->xdi_adapter.trapped) {
-               p = "trapped";
-       } else if (a->xdi_adapter.Initialized) {
-               p = "active";
-       } else {
-               p = "ready";
-       }
-       seq_printf(m, "State       : %s\n", p);
-
-       return 0;
-}
-
-static int info_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, info_proc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations info_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = info_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = info_proc_write,
-};
-
-/*
-** adapter proc init/de-init
-*/
-
-/* --------------------------------------------------------------------------
-   Create adapter directory and files in proc file system
-   -------------------------------------------------------------------------- */
-int create_adapter_proc(diva_os_xdi_adapter_t *a)
-{
-       struct proc_dir_entry *de, *pe;
-       char tmp[16];
-
-       sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
-       if (!(de = proc_mkdir(tmp, proc_net_eicon)))
-               return (0);
-       a->proc_adapter_dir = (void *) de;
-
-       pe = proc_create_data(info_proc_name, S_IRUGO | S_IWUSR, de,
-                             &info_proc_fops, a);
-       if (!pe)
-               return (0);
-       a->proc_info = (void *) pe;
-
-       pe = proc_create_data(grp_opt_proc_name, S_IRUGO | S_IWUSR, de,
-                             &grp_opt_proc_fops, a);
-       if (pe)
-               a->proc_grp_opt = (void *) pe;
-       pe = proc_create_data(d_l1_down_proc_name, S_IRUGO | S_IWUSR, de,
-                             &d_l1_down_proc_fops, a);
-       if (pe)
-               a->proc_d_l1_down = (void *) pe;
-
-       DBG_TRC(("proc entry %s created", tmp));
-
-       return (1);
-}
-
-/* --------------------------------------------------------------------------
-   Remove adapter directory and files in proc file system
-   -------------------------------------------------------------------------- */
-void remove_adapter_proc(diva_os_xdi_adapter_t *a)
-{
-       char tmp[16];
-
-       if (a->proc_adapter_dir) {
-               if (a->proc_d_l1_down) {
-                       remove_proc_entry(d_l1_down_proc_name,
-                                         (struct proc_dir_entry *) a->proc_adapter_dir);
-               }
-               if (a->proc_grp_opt) {
-                       remove_proc_entry(grp_opt_proc_name,
-                                         (struct proc_dir_entry *) a->proc_adapter_dir);
-               }
-               if (a->proc_info) {
-                       remove_proc_entry(info_proc_name,
-                                         (struct proc_dir_entry *) a->proc_adapter_dir);
-               }
-               sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
-               remove_proc_entry(tmp, proc_net_eicon);
-               DBG_TRC(("proc entry %s%d removed", adapter_dir_name,
-                        a->controller));
-       }
-}
diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h
deleted file mode 100644 (file)
index dd6b53a..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_SYNC__H
-#define __DIVA_SYNC__H
-#define IDI_SYNC_REQ_REMOVE             0x00
-#define IDI_SYNC_REQ_GET_NAME           0x01
-#define IDI_SYNC_REQ_GET_SERIAL         0x02
-#define IDI_SYNC_REQ_SET_POSTCALL       0x03
-#define IDI_SYNC_REQ_GET_XLOG           0x04
-#define IDI_SYNC_REQ_GET_FEATURES       0x05
-#define IDI_SYNC_REQ_USB_REGISTER       0x06
-#define IDI_SYNC_REQ_USB_RELEASE        0x07
-#define IDI_SYNC_REQ_USB_ADD_DEVICE     0x08
-#define IDI_SYNC_REQ_USB_START_DEVICE   0x09
-#define IDI_SYNC_REQ_USB_STOP_DEVICE    0x0A
-#define IDI_SYNC_REQ_USB_REMOVE_DEVICE  0x0B
-#define IDI_SYNC_REQ_GET_CARDTYPE       0x0C
-#define IDI_SYNC_REQ_GET_DBG_XLOG       0x0D
-#define DIVA_USB
-#define DIVA_USB_REQ                    0xAC
-#define DIVA_USB_TEST                   0xAB
-#define DIVA_USB_ADD_ADAPTER            0xAC
-#define DIVA_USB_REMOVE_ADAPTER         0xAD
-#define IDI_SYNC_REQ_SERIAL_HOOK        0x80
-#define IDI_SYNC_REQ_XCHANGE_STATUS     0x81
-#define IDI_SYNC_REQ_USB_HOOK           0x82
-#define IDI_SYNC_REQ_PORTDRV_HOOK       0x83
-#define IDI_SYNC_REQ_SLI                0x84   /*  SLI request from 3signal modem drivers */
-#define IDI_SYNC_REQ_RECONFIGURE        0x85
-#define IDI_SYNC_REQ_RESET              0x86
-#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA     0x87
-#define IDI_SYNC_REQ_LOCK_85X                   0x88
-#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99
-#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ   0x98
-#define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE      0xA0
-/******************************************************************************/
-#define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES  0x92
-/*
-  To receive XDI features:
-  1. set 'buffer_length_in_bytes' to length of you buffer
-  2. set 'features' to pointer to your buffer
-  3. issue synchronous request to XDI
-  4. Check that feature 'DIVA_XDI_EXTENDED_FEATURES_VALID' is present
-  after call. This feature does indicate that your request
-  was processed and XDI does support this synchronous request
-  5. if on return bit 31 (0x80000000) in 'buffer_length_in_bytes' is
-  set then provided buffer was too small, and bits 30-0 does
-  contain necessary length of buffer.
-  in this case only features that do find place in the buffer
-  are indicated to caller
-*/
-typedef struct _diva_xdi_get_extended_xdi_features {
-       dword buffer_length_in_bytes;
-       byte  *features;
-} diva_xdi_get_extended_xdi_features_t;
-/*
-  features[0]
-*/
-#define DIVA_XDI_EXTENDED_FEATURES_VALID          0x01
-#define DIVA_XDI_EXTENDED_FEATURE_CMA             0x02
-#define DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR       0x04
-#define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS       0x08
-#define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC    0x10
-#define DIVA_XDI_EXTENDED_FEATURE_RX_DMA          0x20
-#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA  0x40
-#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID         0x80
-#define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ    1
-/******************************************************************************/
-#define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR   0x93
-typedef struct _diva_xdi_get_adapter_sdram_bar {
-       dword bar;
-} diva_xdi_get_adapter_sdram_bar_t;
-/******************************************************************************/
-#define IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS   0x94
-/*
-  CAPI Parameters will be written in the caller's buffer
-*/
-typedef struct _diva_xdi_get_capi_parameters {
-       dword structure_length;
-       byte flag_dynamic_l1_down;
-       byte group_optimization_enabled;
-} diva_xdi_get_capi_parameters_t;
-/******************************************************************************/
-#define IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER   0x95
-/*
-  Get logical adapter number, as assigned by XDI
-  'controller' is starting with zero 'sub' controller number
-  in case of one adapter that supports multiple interfaces
-  'controller' is zero for Master adapter (and adapter that supports
-  only one interface)
-*/
-typedef struct _diva_xdi_get_logical_adapter_number {
-       dword logical_adapter_number;
-       dword controller;
-       dword total_controllers;
-} diva_xdi_get_logical_adapter_number_s_t;
-/******************************************************************************/
-#define IDI_SYNC_REQ_UP1DM_OPERATION   0x96
-/******************************************************************************/
-#define IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION 0x97
-#define IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC     0x01
-#define IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE      0x02
-typedef struct _diva_xdi_dma_descriptor_operation {
-       int operation;
-       int descriptor_number;
-       void *descriptor_address;
-       dword descriptor_magic;
-} diva_xdi_dma_descriptor_operation_t;
-/******************************************************************************/
-#define IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY   0x01
-#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY     0x02
-#define IDI_SYNC_REQ_DIDD_ADD_ADAPTER               0x03
-#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER            0x04
-#define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY        0x05
-#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC           0x10
-typedef struct _diva_didd_adapter_notify {
-       dword handle; /* Notification handle */
-       void *callback;
-       void *context;
-} diva_didd_adapter_notify_t;
-typedef struct _diva_didd_add_adapter {
-       void *descriptor;
-} diva_didd_add_adapter_t;
-typedef struct _diva_didd_remove_adapter {
-       IDI_CALL p_request;
-} diva_didd_remove_adapter_t;
-typedef struct _diva_didd_read_adapter_array {
-       void *buffer;
-       dword length;
-} diva_didd_read_adapter_array_t;
-typedef struct _diva_didd_get_cfg_lib_ifc {
-       void *ifc;
-} diva_didd_get_cfg_lib_ifc_t;
-/******************************************************************************/
-#define IDI_SYNC_REQ_XDI_GET_STREAM    0x91
-#define DIVA_XDI_SYNCHRONOUS_SERVICE   0x01
-#define DIVA_XDI_DMA_SERVICE           0x02
-#define DIVA_XDI_AUTO_SERVICE          0x03
-#define DIVA_ISTREAM_COMPLETE_NOTIFY   0
-#define DIVA_ISTREAM_COMPLETE_READ     1
-#define DIVA_ISTREAM_COMPLETE_WRITE    2
-typedef struct _diva_xdi_stream_interface {
-       unsigned char  Id;                 /* filled by XDI client */
-       unsigned char provided_service;    /* filled by XDI        */
-       unsigned char requested_service;   /* filled by XDI Client */
-       void *xdi_context;    /* filled by XDI */
-       void *client_context;   /* filled by XDI client */
-       int (*write)(void *context,
-                    int Id,
-                    void *data,
-                    int length,
-                    int final,
-                    byte usr1,
-                    byte usr2);
-       int (*read)(void *context,
-                   int Id,
-                   void *data,
-                   int max_length,
-                   int *final,
-                   byte *usr1,
-                   byte *usr2);
-       int (*complete)(void *client_context,
-                       int Id,
-                       int what,
-                       void *data,
-                       int length,
-                       int *final);
-} diva_xdi_stream_interface_t;
-/******************************************************************************/
-/*
- * IDI_SYNC_REQ_SERIAL_HOOK - special interface for the DIVA Mobile card
- */
-typedef struct
-{ unsigned char LineState;         /* Modem line state (STATUS_R) */
-#define SERIAL_GSM_CELL 0x01   /* GSM or CELL cable attached  */
-       unsigned char CardState;          /* PCMCIA card state (0 = down) */
-       unsigned char IsdnState;          /* ISDN layer 1 state (0 = down)*/
-       unsigned char HookState;          /* current logical hook state */
-#define SERIAL_ON_HOOK 0x02   /* set in DIVA CTRL_R register */
-} SERIAL_STATE;
-typedef int (*SERIAL_INT_CB)(void *Context);
-typedef int (*SERIAL_DPC_CB)(void *Context);
-typedef unsigned char (*SERIAL_I_SYNC)(void *Context);
-typedef struct
-{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
-       unsigned char Req;             /* request (must be always 0) */
-       unsigned char Rc;              /* return code (is the request) */
-       unsigned char Function;           /* private function code  */
-#define SERIAL_HOOK_ATTACH 0x81
-#define SERIAL_HOOK_STATUS 0x82
-#define SERIAL_HOOK_I_SYNC 0x83
-#define SERIAL_HOOK_NOECHO 0x84
-#define SERIAL_HOOK_RING 0x85
-#define SERIAL_HOOK_DETACH 0x8f
-       unsigned char Flags;           /* function refinements   */
-       /* parameters passed by the ATTACH request      */
-       SERIAL_INT_CB InterruptHandler; /* called on each interrupt  */
-       SERIAL_DPC_CB DeferredHandler; /* called on hook state changes */
-       void   *HandlerContext; /* context for both handlers */
-       /* return values for both the ATTACH and the STATUS request   */
-       unsigned long IoBase;    /* IO port assigned to UART  */
-       SERIAL_STATE State;
-       /* parameters and return values for the I_SYNC function    */
-       SERIAL_I_SYNC SyncFunction;  /* to be called synchronized */
-       void   *SyncContext;  /* context for this function */
-       unsigned char SyncResult;   /* return value of function  */
-} SERIAL_HOOK;
-/*
- * IDI_SYNC_REQ_XCHANGE_STATUS - exchange the status between IDI and WMP
- * IDI_SYNC_REQ_RECONFIGURE - reconfiguration of IDI from WMP
- */
-typedef struct
-{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
-       unsigned char Req;             /* request (must be always 0) */
-       unsigned char Rc;              /* return code (is the request) */
-#define DRIVER_STATUS_BOOT  0xA1
-#define DRIVER_STATUS_INIT_DEV 0xA2
-#define DRIVER_STATUS_RUNNING 0xA3
-#define DRIVER_STATUS_SHUTDOWN 0xAF
-#define DRIVER_STATUS_TRAPPED 0xAE
-       unsigned char wmpStatus;          /* exported by WMP              */
-       unsigned char idiStatus;   /* exported by IDI              */
-       unsigned long wizProto;   /* from WMP registry to IDI     */
-       /* the cardtype value is defined by cardtype.h */
-       unsigned long cardType;   /* from IDI registry to WMP     */
-       unsigned long nt2;    /* from IDI registry to WMP     */
-       unsigned long permanent;   /* from IDI registry to WMP     */
-       unsigned long stableL2;   /* from IDI registry to WMP     */
-       unsigned long tei;    /* from IDI registry to WMP     */
-#define CRC4_MASK   0x00000003
-#define L1_TRISTATE_MASK 0x00000004
-#define WATCHDOG_MASK  0x00000008
-#define NO_ORDER_CHECK_MASK 0x00000010
-#define LOW_CHANNEL_MASK 0x00000020
-#define NO_HSCX30_MASK  0x00000040
-#define SET_BOARD   0x00001000
-#define SET_CRC4   0x00030000
-#define SET_L1_TRISTATE  0x00040000
-#define SET_WATCHDOG  0x00080000
-#define SET_NO_ORDER_CHECK 0x00100000
-#define SET_LOW_CHANNEL  0x00200000
-#define SET_NO_HSCX30  0x00400000
-#define SET_MODE   0x00800000
-#define SET_PROTO   0x02000000
-#define SET_CARDTYPE  0x04000000
-#define SET_NT2    0x08000000
-#define SET_PERMANENT  0x10000000
-#define SET_STABLEL2  0x20000000
-#define SET_TEI    0x40000000
-#define SET_NUMBERLEN  0x80000000
-       unsigned long Flag;  /* |31-Type-16|15-Mask-0| */
-       unsigned long NumberLen; /* reconfiguration: union is empty */
-       union {
-               struct {    /* possible reconfiguration, but ... ; SET_BOARD */
-                       unsigned long SerialNumber;
-                       char     *pCardname; /* di_defs.h: BOARD_NAME_LENGTH */
-               } board;
-               struct {      /* reset: need resources */
-                       void *pRawResources;
-                       void *pXlatResources;
-               } res;
-               struct { /* reconfiguration: wizProto == PROTTYPE_RBSCAS */
-#define GLARE_RESOLVE_MASK 0x00000001
-#define DID_MASK   0x00000002
-#define BEARER_CAP_MASK  0x0000000c
-#define SET_GLARE_RESOLVE 0x00010000
-#define SET_DID    0x00020000
-#define SET_BEARER_CAP  0x000c0000
-                       unsigned long Flag;  /* |31-Type-16|15-VALUE-0| */
-                       unsigned short DigitTimeout;
-                       unsigned short AnswerDelay;
-               } rbs;
-               struct { /* reconfiguration: wizProto == PROTTYPE_QSIG */
-#define CALL_REF_LENGTH1_MASK 0x00000001
-#define BRI_CHANNEL_ID_MASK  0x00000002
-#define SET_CALL_REF_LENGTH  0x00010000
-#define SET_BRI_CHANNEL_ID  0x00020000
-                       unsigned long Flag;  /* |31-Type-16|15-VALUE-0| */
-               } qsig;
-               struct { /* reconfiguration: NumberLen != 0 */
-#define SET_SPID1   0x00010000
-#define SET_NUMBER1   0x00020000
-#define SET_SUBADDRESS1  0x00040000
-#define SET_SPID2   0x00100000
-#define SET_NUMBER2   0x00200000
-#define SET_SUBADDRESS2  0x00400000
-#define MASK_SET   0xffff0000
-                       unsigned long Flag;   /* |31-Type-16|15-Channel-0| */
-                       unsigned char *pBuffer; /* number value */
-               } isdnNo;
-       }
-               parms
-               ;
-} isdnProps;
-/*
- * IDI_SYNC_REQ_PORTDRV_HOOK - signal plug/unplug (Award Cardware only)
- */
-typedef void (*PORTDRV_HOOK_CB)(void *Context, int Plug);
-typedef struct
-{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
-       unsigned char Req;             /* request (must be always 0) */
-       unsigned char Rc;              /* return code (is the request) */
-       unsigned char Function;           /* private function code  */
-       unsigned char Flags;           /* function refinements   */
-       PORTDRV_HOOK_CB Callback;   /* to be called on plug/unplug */
-       void   *Context;   /* context for callback   */
-       unsigned long Info;    /* more info if needed   */
-} PORTDRV_HOOK;
-/*  Codes for the 'Rc' element in structure below. */
-#define SLI_INSTALL     (0xA1)
-#define SLI_UNINSTALL   (0xA2)
-typedef int (*SLIENTRYPOINT)(void *p3SignalAPI, void *pContext);
-typedef struct
-{   /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
-       unsigned char   Req;                /* request (must be always 0)   */
-       unsigned char   Rc;                 /* return code (is the request) */
-       unsigned char   Function;           /* private function code        */
-       unsigned char   Flags;              /* function refinements         */
-       SLIENTRYPOINT   Callback;           /* to be called on plug/unplug  */
-       void            *Context;           /* context for callback         */
-       unsigned long   Info;               /* more info if needed          */
-} SLIENTRYPOINT_REQ;
-/******************************************************************************/
-/*
- *  Definitions for DIVA USB
- */
-typedef int (*USB_SEND_REQ)(unsigned char PipeIndex, unsigned char Type, void *Data, int sizeData);
-typedef int (*USB_START_DEV)(void *Adapter, void *Ipac);
-/* called from WDM */
-typedef void (*USB_RECV_NOTIFY)(void *Ipac, void *msg);
-typedef void (*USB_XMIT_NOTIFY)(void *Ipac, unsigned char PipeIndex);
-/******************************************************************************/
-/*
- * Parameter description for synchronous requests.
- *
- * Sorry, must repeat some parts of di_defs.h here because
- * they are not defined for all operating environments
- */
-typedef union
-{ ENTITY Entity;
-       struct
-       { /* 'Req' and 'Rc' are at the same place as in the ENTITY struct */
-               unsigned char   Req; /* request (must be always 0) */
-               unsigned char   Rc;  /* return code (is the request) */
-       }   Request;
-       struct
-       { unsigned char   Req; /* request (must be always 0) */
-               unsigned char   Rc;  /* return code (0x01)   */
-               unsigned char   name[BOARD_NAME_LENGTH];
-       }   GetName;
-       struct
-       { unsigned char   Req; /* request (must be always 0) */
-               unsigned char   Rc;  /* return code (0x02)   */
-               unsigned long   serial; /* serial number    */
-       }   GetSerial;
-       struct
-       { unsigned char   Req; /* request (must be always 0) */
-               unsigned char   Rc;  /* return code (0x02)   */
-               unsigned long   lineIdx;/* line, 0 if card has only one */
-       }   GetLineIdx;
-       struct
-       { unsigned char  Req;     /* request (must be always 0) */
-               unsigned char  Rc;      /* return code (0x02)   */
-               unsigned long  cardtype;/* card type        */
-       }   GetCardType;
-       struct
-       { unsigned short command;/* command = 0x0300 */
-               unsigned short dummy; /* not used */
-               IDI_CALL       callback;/* routine to call back */
-               ENTITY      *contxt; /* ptr to entity to use */
-       }   PostCall;
-       struct
-       { unsigned char  Req;  /* request (must be always 0) */
-               unsigned char  Rc;   /* return code (0x04)   */
-               unsigned char  pcm[1]; /* buffer (a pc_maint struct) */
-       }   GetXlog;
-       struct
-       { unsigned char  Req;  /* request (must be always 0) */
-               unsigned char  Rc;   /* return code (0x05)   */
-               unsigned short features;/* feature defines see below */
-       }   GetFeatures;
-       SERIAL_HOOK  SerialHook;
-/* Added for DIVA USB */
-       struct
-       { unsigned char   Req;
-               unsigned char   Rc;
-               USB_SEND_REQ    UsbSendRequest; /* function in Diva Usb WDM driver in usb_os.c, */
-               /* called from usb_drv.c to send a message to our device */
-               /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0); */
-               USB_RECV_NOTIFY usb_recv;       /* called from usb_os.c to pass a received message and ptr to IPAC */
-               /* on to usb_drv.c by a call to usb_recv(). */
-               USB_XMIT_NOTIFY usb_xmit;       /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
-               /* to usb_drv.c by a call to usb_xmit(). */
-               USB_START_DEV   UsbStartDevice; /* Start the USB Device, in usb_os.c */
-               IDI_CALL        callback;       /* routine to call back */
-               ENTITY          *contxt;     /* ptr to entity to use */
-               void **ipac_ptr;    /* pointer to struct IPAC in VxD */
-       } Usb_Msg_old;
-/* message used by WDM and VXD to pass pointers of function and IPAC* */
-       struct
-       { unsigned char Req;
-               unsigned char Rc;
-               USB_SEND_REQ    pUsbSendRequest;/* function in Diva Usb WDM driver in usb_os.c, */
-               /* called from usb_drv.c to send a message to our device */
-               /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0); */
-               USB_RECV_NOTIFY p_usb_recv;     /* called from usb_os.c to pass a received message and ptr to IPAC */
-               /* on to usb_drv.c by a call to usb_recv(). */
-               USB_XMIT_NOTIFY p_usb_xmit;     /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
-               /* to usb_drv.c by a call to usb_xmit().*/
-               void            *ipac_ptr;      /* &Diva.ipac pointer to struct IPAC in VxD */
-       } Usb_Msg;
-       PORTDRV_HOOK PortdrvHook;
-       SLIENTRYPOINT_REQ   sliEntryPointReq;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_stream_interface_t info;
-       } xdi_stream_info;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_get_extended_xdi_features_t info;
-       } xdi_extended_features;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_get_adapter_sdram_bar_t info;
-       } xdi_sdram_bar;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_get_capi_parameters_t info;
-       } xdi_capi_prms;
-       struct {
-               ENTITY           e;
-               diva_didd_adapter_notify_t info;
-       } didd_notify;
-       struct {
-               ENTITY           e;
-               diva_didd_add_adapter_t   info;
-       } didd_add_adapter;
-       struct {
-               ENTITY           e;
-               diva_didd_remove_adapter_t info;
-       } didd_remove_adapter;
-       struct {
-               ENTITY             e;
-               diva_didd_read_adapter_array_t info;
-       } didd_read_adapter_array;
-       struct {
-               ENTITY             e;
-               diva_didd_get_cfg_lib_ifc_t     info;
-       } didd_get_cfg_lib_ifc;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_get_logical_adapter_number_s_t info;
-       } xdi_logical_adapter_number;
-       struct {
-               unsigned char Req;
-               unsigned char Rc;
-               diva_xdi_dma_descriptor_operation_t info;
-       } xdi_dma_descriptor_operation;
-} IDI_SYNC_REQ;
-/******************************************************************************/
-#endif /* __DIVA_SYNC__H */
diff --git a/drivers/isdn/hardware/eicon/dqueue.c b/drivers/isdn/hardware/eicon/dqueue.c
deleted file mode 100644 (file)
index 7958a25..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/* $Id: dqueue.c,v 1.5 2003/04/12 21:40:49 schindler Exp $
- *
- * Driver for Eicon DIVA Server ISDN cards.
- * User Mode IDI Interface
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include "platform.h"
-#include "dqueue.h"
-
-int
-diva_data_q_init(diva_um_idi_data_queue_t *q,
-                int max_length, int max_segments)
-{
-       int i;
-
-       q->max_length = max_length;
-       q->segments = max_segments;
-
-       for (i = 0; i < q->segments; i++) {
-               q->data[i] = NULL;
-               q->length[i] = 0;
-       }
-       q->read = q->write = q->count = q->segment_pending = 0;
-
-       for (i = 0; i < q->segments; i++) {
-               if (!(q->data[i] = diva_os_malloc(0, q->max_length))) {
-                       diva_data_q_finit(q);
-                       return (-1);
-               }
-       }
-
-       return (0);
-}
-
-int diva_data_q_finit(diva_um_idi_data_queue_t *q)
-{
-       int i;
-
-       for (i = 0; i < q->segments; i++) {
-               if (q->data[i]) {
-                       diva_os_free(0, q->data[i]);
-               }
-               q->data[i] = NULL;
-               q->length[i] = 0;
-       }
-       q->read = q->write = q->count = q->segment_pending = 0;
-
-       return (0);
-}
-
-int diva_data_q_get_max_length(const diva_um_idi_data_queue_t *q)
-{
-       return (q->max_length);
-}
-
-void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t *q)
-{
-       if ((!q->segment_pending) && (q->count < q->segments)) {
-               q->segment_pending = 1;
-               return (q->data[q->write]);
-       }
-
-       return NULL;
-}
-
-void
-diva_data_q_ack_segment4write(diva_um_idi_data_queue_t *q, int length)
-{
-       if (q->segment_pending) {
-               q->length[q->write] = length;
-               q->count++;
-               q->write++;
-               if (q->write >= q->segments) {
-                       q->write = 0;
-               }
-               q->segment_pending = 0;
-       }
-}
-
-const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
-                                        q)
-{
-       if (q->count) {
-               return (q->data[q->read]);
-       }
-       return NULL;
-}
-
-int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t *q)
-{
-       return (q->length[q->read]);
-}
-
-void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t *q)
-{
-       if (q->count) {
-               q->length[q->read] = 0;
-               q->count--;
-               q->read++;
-               if (q->read >= q->segments) {
-                       q->read = 0;
-               }
-       }
-}
diff --git a/drivers/isdn/hardware/eicon/dqueue.h b/drivers/isdn/hardware/eicon/dqueue.h
deleted file mode 100644 (file)
index 2da9799..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: dqueue.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
-
-#ifndef _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
-#define _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
-
-#define DIVA_UM_IDI_MAX_MSGS 64
-
-typedef struct _diva_um_idi_data_queue {
-       int segments;
-       int max_length;
-       int read;
-       int write;
-       int count;
-       int segment_pending;
-       void *data[DIVA_UM_IDI_MAX_MSGS];
-       int length[DIVA_UM_IDI_MAX_MSGS];
-} diva_um_idi_data_queue_t;
-
-int diva_data_q_init(diva_um_idi_data_queue_t *q,
-                    int max_length, int max_segments);
-int diva_data_q_finit(diva_um_idi_data_queue_t *q);
-int diva_data_q_get_max_length(const diva_um_idi_data_queue_t *q);
-void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t *q);
-void diva_data_q_ack_segment4write(diva_um_idi_data_queue_t *q,
-                                  int length);
-const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
-                                        q);
-int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t *q);
-void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t *q);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/dsp_defs.h b/drivers/isdn/hardware/eicon/dsp_defs.h
deleted file mode 100644 (file)
index 94828c8..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef DSP_DEFS_H_
-#define DSP_DEFS_H_
-#include "dspdids.h"
-/*---------------------------------------------------------------------------*/
-#define dsp_download_reserve_space(fp, length)
-/*****************************************************************************/
-/*
- * OS file access abstraction layer
- *
- * I/O functions returns -1 on error, 0 on EOF
- */
-struct _OsFileHandle_;
-typedef long (*OsFileIo)(struct _OsFileHandle_ *handle,
-                        void *buffer,
-                        long size);
-typedef long (*OsFileSeek)(struct _OsFileHandle_ *handle,
-                          long position,
-                          int mode);
-typedef long (*OsCardLoad)(struct _OsFileHandle_    *handle,
-                          long length,
-                          void **addr);
-typedef struct _OsFileHandle_
-{ void       *sysFileDesc;
-       unsigned long sysFileSize;
-       OsFileIo      sysFileRead;
-       OsFileSeek    sysFileSeek;
-       void       *sysLoadDesc;
-       OsCardLoad    sysCardLoad;
-} OsFileHandle;
-extern OsFileHandle *OsOpenFile(char *path_name);
-extern void          OsCloseFile(OsFileHandle *fp);
-/*****************************************************************************/
-#define DSP_TELINDUS_FILE "dspdload.bin"
-/* special DSP file for BRI cards for Qsig and CornetN because of missing memory */
-#define DSP_QSIG_TELINDUS_FILE "dspdqsig.bin"
-#define DSP_MDM_TELINDUS_FILE "dspdvmdm.bin"
-#define DSP_FAX_TELINDUS_FILE "dspdvfax.bin"
-#define DSP_DIRECTORY_ENTRIES 64
-#define DSP_MEMORY_TYPE_EXTERNAL_DM         0
-#define DSP_MEMORY_TYPE_EXTERNAL_PM         1
-#define DSP_MEMORY_TYPE_INTERNAL_DM         2
-#define DSP_MEMORY_TYPE_INTERNAL_PM         3
-#define DSP_DOWNLOAD_FLAG_BOOTABLE          0x0001
-#define DSP_DOWNLOAD_FLAG_2181              0x0002
-#define DSP_DOWNLOAD_FLAG_TIMECRITICAL      0x0004
-#define DSP_DOWNLOAD_FLAG_COMPAND           0x0008
-#define DSP_MEMORY_BLOCK_COUNT              16
-#define DSP_SEGMENT_PM_FLAG                 0x0001
-#define DSP_SEGMENT_SHARED_FLAG             0x0002
-#define DSP_SEGMENT_EXTERNAL_DM             DSP_MEMORY_TYPE_EXTERNAL_DM
-#define DSP_SEGMENT_EXTERNAL_PM             DSP_MEMORY_TYPE_EXTERNAL_PM
-#define DSP_SEGMENT_INTERNAL_DM             DSP_MEMORY_TYPE_INTERNAL_DM
-#define DSP_SEGMENT_INTERNAL_PM             DSP_MEMORY_TYPE_INTERNAL_PM
-#define DSP_SEGMENT_FIRST_RELOCATABLE       4
-#define DSP_DATA_BLOCK_PM_FLAG              0x0001
-#define DSP_DATA_BLOCK_DWORD_FLAG           0x0002
-#define DSP_DATA_BLOCK_RESOLVE_FLAG         0x0004
-#define DSP_RELOC_NONE                      0x00
-#define DSP_RELOC_SEGMENT_MASK              0x3f
-#define DSP_RELOC_TYPE_MASK                 0xc0
-#define DSP_RELOC_TYPE_0                    0x00  /* relocation of address in DM word / high part of PM word */
-#define DSP_RELOC_TYPE_1                    0x40  /* relocation of address in low part of PM data word */
-#define DSP_RELOC_TYPE_2                    0x80  /* relocation of address in standard command */
-#define DSP_RELOC_TYPE_3                    0xc0  /* relocation of address in call/jump on flag in */
-#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
-#define DSP_COMBIFILE_FORMAT_VERSION_BCD    0x0100
-#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
-#define DSP_FILE_FORMAT_VERSION_BCD         0x0100
-typedef struct tag_dsp_combifile_header
-{
-       char                  format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE];
-       word                  format_version_bcd;
-       word                  header_size;
-       word                  combifile_description_size;
-       word                  directory_entries;
-       word                  directory_size;
-       word                  download_count;
-       word                  usage_mask_size;
-} t_dsp_combifile_header;
-typedef struct tag_dsp_combifile_directory_entry
-{
-       word                  card_type_number;
-       word                  file_set_number;
-} t_dsp_combifile_directory_entry;
-typedef struct tag_dsp_file_header
-{
-       char                  format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE];
-       word                  format_version_bcd;
-       word                  download_id;
-       word                  download_flags;
-       word                  required_processing_power;
-       word                  interface_channel_count;
-       word                  header_size;
-       word                  download_description_size;
-       word                  memory_block_table_size;
-       word                  memory_block_count;
-       word                  segment_table_size;
-       word                  segment_count;
-       word                  symbol_table_size;
-       word                  symbol_count;
-       word                  total_data_size_dm;
-       word                  data_block_count_dm;
-       word                  total_data_size_pm;
-       word                  data_block_count_pm;
-} t_dsp_file_header;
-typedef struct tag_dsp_memory_block_desc
-{
-       word                  alias_memory_block;
-       word                  memory_type;
-       word                  address;
-       word                  size;             /* DSP words */
-} t_dsp_memory_block_desc;
-typedef struct tag_dsp_segment_desc
-{
-       word                  memory_block;
-       word                  attributes;
-       word                  base;
-       word                  size;
-       word                  alignment;        /* ==0 -> no other legal start address than base */
-} t_dsp_segment_desc;
-typedef struct tag_dsp_symbol_desc
-{
-       word                  symbol_id;
-       word                  segment;
-       word                  offset;
-       word                  size;             /* DSP words */
-} t_dsp_symbol_desc;
-typedef struct tag_dsp_data_block_header
-{
-       word                  attributes;
-       word                  segment;
-       word                  offset;
-       word                  size;             /* DSP words */
-} t_dsp_data_block_header;
-typedef struct tag_dsp_download_desc
-{
-       word                  download_id;
-       word                  download_flags;
-       word                  required_processing_power;
-       word                  interface_channel_count;
-       word                  excess_header_size;
-       word                  memory_block_count;
-       word                  segment_count;
-       word                  symbol_count;
-       word                  data_block_count_dm;
-       word                  data_block_count_pm;
-       byte *p_excess_header_data;
-       char *p_download_description;
-       t_dsp_memory_block_desc *p_memory_block_table;
-       t_dsp_segment_desc *p_segment_table;
-       t_dsp_symbol_desc *p_symbol_table;
-       word *p_data_blocks_dm;
-       word *p_data_blocks_pm;
-} t_dsp_desc;
-typedef struct tag_dsp_portable_download_desc /* be sure to keep native alignment for MAESTRA's */
-{
-       word                  download_id;
-       word                  download_flags;
-       word                  required_processing_power;
-       word                  interface_channel_count;
-       word                  excess_header_size;
-       word                  memory_block_count;
-       word                  segment_count;
-       word                  symbol_count;
-       word                  data_block_count_dm;
-       word                  data_block_count_pm;
-       dword                 p_excess_header_data;
-       dword                 p_download_description;
-       dword                 p_memory_block_table;
-       dword                 p_segment_table;
-       dword                 p_symbol_table;
-       dword                 p_data_blocks_dm;
-       dword                 p_data_blocks_pm;
-} t_dsp_portable_desc;
-#define DSP_DOWNLOAD_INDEX_KERNEL               0
-#define DSP30TX_DOWNLOAD_INDEX_KERNEL           1
-#define DSP30RX_DOWNLOAD_INDEX_KERNEL           2
-#define DSP_MAX_DOWNLOAD_COUNT                  64
-#define DSP_DOWNLOAD_MAX_SEGMENTS         16
-#define DSP_UDATA_REQUEST_RECONFIGURE     0
-/*
-  parameters:
-  <word> reconfigure delay (in 8kHz samples)
-  <word> reconfigure code
-  <byte> reconfigure hdlc preamble flags
-*/
-#define DSP_RECONFIGURE_TX_FLAG           0x8000
-#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG  0x4000
-#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
-#define DSP_RECONFIGURE_HDLC_FLAG         0x1000
-#define DSP_RECONFIGURE_SYNC_FLAG         0x0800
-#define DSP_RECONFIGURE_PROTOCOL_MASK     0x00ff
-#define DSP_RECONFIGURE_IDLE              0
-#define DSP_RECONFIGURE_V25               1
-#define DSP_RECONFIGURE_V21_CH2           2
-#define DSP_RECONFIGURE_V27_2400          3
-#define DSP_RECONFIGURE_V27_4800          4
-#define DSP_RECONFIGURE_V29_7200          5
-#define DSP_RECONFIGURE_V29_9600          6
-#define DSP_RECONFIGURE_V33_12000         7
-#define DSP_RECONFIGURE_V33_14400         8
-#define DSP_RECONFIGURE_V17_7200          9
-#define DSP_RECONFIGURE_V17_9600          10
-#define DSP_RECONFIGURE_V17_12000         11
-#define DSP_RECONFIGURE_V17_14400         12
-/*
-  data indications if transparent framer
-  <byte> data 0
-  <byte> data 1
-  ...
-  data indications if HDLC framer
-  <byte> data 0
-  <byte> data 1
-  ...
-  <byte> CRC 0
-  <byte> CRC 1
-  <byte> preamble flags
-*/
-#define DSP_UDATA_INDICATION_SYNC         0
-/*
-  returns:
-  <word> time of sync (sampled from counter at 8kHz)
-*/
-#define DSP_UDATA_INDICATION_DCD_OFF      1
-/*
-  returns:
-  <word> time of DCD off (sampled from counter at 8kHz)
-*/
-#define DSP_UDATA_INDICATION_DCD_ON       2
-/*
-  returns:
-  <word> time of DCD on (sampled from counter at 8kHz)
-  <byte> connected norm
-  <word> connected options
-  <dword> connected speed (bit/s)
-*/
-#define DSP_UDATA_INDICATION_CTS_OFF      3
-/*
-  returns:
-  <word> time of CTS off (sampled from counter at 8kHz)
-*/
-#define DSP_UDATA_INDICATION_CTS_ON       4
-/*
-  returns:
-  <word> time of CTS on (sampled from counter at 8kHz)
-  <byte> connected norm
-  <word> connected options
-  <dword> connected speed (bit/s)
-*/
-#define DSP_CONNECTED_NORM_UNSPECIFIED      0
-#define DSP_CONNECTED_NORM_V21              1
-#define DSP_CONNECTED_NORM_V23              2
-#define DSP_CONNECTED_NORM_V22              3
-#define DSP_CONNECTED_NORM_V22_BIS          4
-#define DSP_CONNECTED_NORM_V32_BIS          5
-#define DSP_CONNECTED_NORM_V34              6
-#define DSP_CONNECTED_NORM_V8               7
-#define DSP_CONNECTED_NORM_BELL_212A        8
-#define DSP_CONNECTED_NORM_BELL_103         9
-#define DSP_CONNECTED_NORM_V29_LEASED_LINE  10
-#define DSP_CONNECTED_NORM_V33_LEASED_LINE  11
-#define DSP_CONNECTED_NORM_TFAST            12
-#define DSP_CONNECTED_NORM_V21_CH2          13
-#define DSP_CONNECTED_NORM_V27_TER          14
-#define DSP_CONNECTED_NORM_V29              15
-#define DSP_CONNECTED_NORM_V33              16
-#define DSP_CONNECTED_NORM_V17              17
-#define DSP_CONNECTED_OPTION_TRELLIS        0x0001
-/*---------------------------------------------------------------------------*/
-extern char *dsp_read_file(OsFileHandle *fp,
-                          word card_type_number,
-                          word *p_dsp_download_count,
-                          t_dsp_desc *p_dsp_download_table,
-                          t_dsp_portable_desc *p_dsp_portable_download_table);
-/*---------------------------------------------------------------------------*/
-#endif /* DSP_DEFS_H_ */
diff --git a/drivers/isdn/hardware/eicon/dsp_tst.h b/drivers/isdn/hardware/eicon/dsp_tst.h
deleted file mode 100644 (file)
index 85edd3e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: dsp_tst.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
-
-#ifndef __DIVA_PRI_HOST_TEST_DSPS_H__
-#define __DIVA_PRI_HOST_TEST_DSPS_H__
-
-/*
-  DSP registers on maestra pri
-*/
-#define DSP1_PORT       (0x00)
-#define DSP2_PORT       (0x8)
-#define DSP3_PORT       (0x800)
-#define DSP4_PORT       (0x808)
-#define DSP5_PORT       (0x810)
-#define DSP6_PORT       (0x818)
-#define DSP7_PORT       (0x820)
-#define DSP8_PORT       (0x828)
-#define DSP9_PORT       (0x830)
-#define DSP10_PORT      (0x840)
-#define DSP11_PORT      (0x848)
-#define DSP12_PORT      (0x850)
-#define DSP13_PORT      (0x858)
-#define DSP14_PORT      (0x860)
-#define DSP15_PORT      (0x868)
-#define DSP16_PORT      (0x870)
-#define DSP17_PORT      (0x1000)
-#define DSP18_PORT      (0x1008)
-#define DSP19_PORT      (0x1010)
-#define DSP20_PORT      (0x1018)
-#define DSP21_PORT      (0x1020)
-#define DSP22_PORT      (0x1028)
-#define DSP23_PORT      (0x1030)
-#define DSP24_PORT      (0x1040)
-#define DSP25_PORT      (0x1048)
-#define DSP26_PORT      (0x1050)
-#define DSP27_PORT      (0x1058)
-#define DSP28_PORT      (0x1060)
-#define DSP29_PORT      (0x1068)
-#define DSP30_PORT      (0x1070)
-#define DSP_ADR_OFFS    0x80
-
-/*------------------------------------------------------------------
-  Dsp related definitions
-  ------------------------------------------------------------------ */
-#define DSP_SIGNATURE_PROBE_WORD 0x5a5a
-#define dsp_make_address_ex(pm, address) ((word)((pm) ? (address) : (address) + 0x4000))
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/dspdids.h b/drivers/isdn/hardware/eicon/dspdids.h
deleted file mode 100644 (file)
index 957b33c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef DSPDIDS_H_
-#define DSPDIDS_H_
-/*---------------------------------------------------------------------------*/
-#define DSP_DID_INVALID   0
-#define DSP_DID_DIVA   1
-#define DSP_DID_DIVA_PRO  2
-#define DSP_DID_DIVA_PRO_20  3
-#define DSP_DID_DIVA_PRO_PCCARD  4
-#define DSP_DID_DIVA_SERVER_BRI_1M 5
-#define DSP_DID_DIVA_SERVER_BRI_2M 6
-#define DSP_DID_DIVA_SERVER_PRI_2M_TX 7
-#define DSP_DID_DIVA_SERVER_PRI_2M_RX 8
-#define DSP_DID_DIVA_SERVER_PRI_30M 9
-#define DSP_DID_TASK_HSCX  100
-#define DSP_DID_TASK_HSCX_PRI_2M_TX 101
-#define DSP_DID_TASK_HSCX_PRI_2M_RX 102
-#define DSP_DID_TASK_V110KRNL  200
-#define DSP_DID_OVERLAY_V1100  201
-#define DSP_DID_OVERLAY_V1101  202
-#define DSP_DID_OVERLAY_V1102  203
-#define DSP_DID_OVERLAY_V1103  204
-#define DSP_DID_OVERLAY_V1104  205
-#define DSP_DID_OVERLAY_V1105  206
-#define DSP_DID_OVERLAY_V1106  207
-#define DSP_DID_OVERLAY_V1107  208
-#define DSP_DID_OVERLAY_V1108  209
-#define DSP_DID_OVERLAY_V1109  210
-#define DSP_DID_TASK_V110_PRI_2M_TX 220
-#define DSP_DID_TASK_V110_PRI_2M_RX 221
-#define DSP_DID_TASK_MODEM  300
-#define DSP_DID_TASK_FAX05  400
-#define DSP_DID_TASK_VOICE  500
-#define DSP_DID_TASK_TIKRNL81  600
-#define DSP_DID_OVERLAY_DIAL  601
-#define DSP_DID_OVERLAY_V22  602
-#define DSP_DID_OVERLAY_V32  603
-#define DSP_DID_OVERLAY_FSK  604
-#define DSP_DID_OVERLAY_FAX  605
-#define DSP_DID_OVERLAY_VXX  606
-#define DSP_DID_OVERLAY_V8  607
-#define DSP_DID_OVERLAY_INFO  608
-#define DSP_DID_OVERLAY_V34  609
-#define DSP_DID_OVERLAY_DFX  610
-#define DSP_DID_PARTIAL_OVERLAY_DIAL 611
-#define DSP_DID_PARTIAL_OVERLAY_FSK 612
-#define DSP_DID_PARTIAL_OVERLAY_FAX 613
-#define DSP_DID_TASK_TIKRNL05  700
-/*---------------------------------------------------------------------------*/
-#endif
-/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/dsrv4bri.h b/drivers/isdn/hardware/eicon/dsrv4bri.h
deleted file mode 100644 (file)
index f353fb6..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_DSRV_4_BRI_INC__
-#define __DIVA_XDI_DSRV_4_BRI_INC__
-/*
- * Some special registers in the PLX 9054
- */
-#define PLX9054_P2LDBELL    0x60
-#define PLX9054_L2PDBELL    0x64
-#define PLX9054_INTCSR      0x69
-#define PLX9054_INT_ENABLE  0x09
-#define PLX9054_SOFT_RESET 0x4000
-#define PLX9054_RELOAD_EEPROM 0x2000
-#define DIVA_4BRI_REVISION(__x__) (((__x__)->cardType == CARDTYPE_DIVASRV_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2F_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI))
-void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter);
-void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter);
-#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_bri.h b/drivers/isdn/hardware/eicon/dsrv_bri.h
deleted file mode 100644 (file)
index 8a67dbc..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_DSRV_BRI_INC__
-#define __DIVA_XDI_DSRV_BRI_INC__
-/*
-  Functions exported from os dependent part of
-  BRI card configuration and used in
-  OS independed part
-*/
-/*
-  Prepare OS dependent part of BRI functions
-*/
-void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter);
-#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_pri.h b/drivers/isdn/hardware/eicon/dsrv_pri.h
deleted file mode 100644 (file)
index fd1a9ff..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_DSRV_PRI_INC__
-#define __DIVA_XDI_DSRV_PRI_INC__
-/*
-  Functions exported from os dependent part of
-  PRI card configuration and used in
-  OS independed part
-*/
-/*
-  Prepare OS dependent part of PRI/PRI Rev.2 functions
-*/
-void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter);
-void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter);
-#endif
diff --git a/drivers/isdn/hardware/eicon/entity.h b/drivers/isdn/hardware/eicon/entity.h
deleted file mode 100644 (file)
index f9767d3..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: entity.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
-
-#ifndef __DIVAS_USER_MODE_IDI_ENTITY__
-#define __DIVAS_USER_MODE_IDI_ENTITY__
-
-#define DIVA_UM_IDI_RC_PENDING      0x00000001
-#define DIVA_UM_IDI_REMOVE_PENDING  0x00000002
-#define DIVA_UM_IDI_TX_FLOW_CONTROL 0x00000004
-#define DIVA_UM_IDI_REMOVED         0x00000008
-#define DIVA_UM_IDI_ASSIGN_PENDING  0x00000010
-
-typedef struct _divas_um_idi_entity {
-       struct list_head          link;
-       diva_um_idi_adapter_t *adapter; /* Back to adapter */
-       ENTITY e;
-       void *os_ref;
-       dword status;
-       void *os_context;
-       int rc_count;
-       diva_um_idi_data_queue_t  data; /* definad by user 1 ... MAX */
-       diva_um_idi_data_queue_t  rc;   /* two entries */
-       BUFFERS                   XData;
-       BUFFERS                   RData;
-       byte                      buffer[2048 + 512];
-} divas_um_idi_entity_t;
-
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/helpers.h b/drivers/isdn/hardware/eicon/helpers.h
deleted file mode 100644 (file)
index c9156b0..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
-#define __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
-dword diva_get_protocol_file_features(byte *File,
-                                     int offset,
-                                     char *IdStringBuffer,
-                                     dword IdBufferSize);
-void diva_configure_protocol(PISDN_ADAPTER IoAdapter);
-/*
-  Low level file access system abstraction
-*/
-/* -------------------------------------------------------------------------
-   Access to single file
-   Return pointer to the image of the requested file,
-   write image length to 'FileLength'
-   ------------------------------------------------------------------------- */
-void *xdiLoadFile(char *FileName, dword *FileLength, unsigned long MaxLoadSize);
-/* -------------------------------------------------------------------------
-   Dependent on the protocol settings does read return pointer
-   to the image of appropriate protocol file
-   ------------------------------------------------------------------------- */
-void *xdiLoadArchive(PISDN_ADAPTER IoAdapter, dword *FileLength, unsigned long MaxLoadSize);
-/* --------------------------------------------------------------------------
-   Free all system resources accessed by xdiLoadFile and xdiLoadArchive
-   -------------------------------------------------------------------------- */
-void xdiFreeFile(void *handle);
-#endif
diff --git a/drivers/isdn/hardware/eicon/idifunc.c b/drivers/isdn/hardware/eicon/idifunc.c
deleted file mode 100644 (file)
index fef6586..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/* $Id: idifunc.c,v 1.14.4.4 2004/08/28 20:03:53 armin Exp $
- *
- * Driver for Eicon DIVA Server ISDN cards.
- * User Mode IDI Interface
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#include "platform.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "um_xdi.h"
-#include "um_idi.h"
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-extern char *DRIVERRELEASE_IDI;
-
-extern void DIVA_DIDD_Read(void *, int);
-extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int);
-extern void diva_user_mode_idi_remove_adapter(int);
-
-static dword notify_handle;
-static DESCRIPTOR DAdapter;
-static DESCRIPTOR MAdapter;
-
-static void no_printf(unsigned char *x, ...)
-{
-       /* dummy debug function */
-}
-
-#include "debuglib.c"
-
-/*
- * stop debug
- */
-static void stop_dbg(void)
-{
-       DbgDeregister();
-       memset(&MAdapter, 0, sizeof(MAdapter));
-       dprintf = no_printf;
-}
-
-typedef struct _udiva_card {
-       struct list_head list;
-       int Id;
-       DESCRIPTOR d;
-} udiva_card;
-
-static LIST_HEAD(cards);
-static diva_os_spin_lock_t ll_lock;
-
-/*
- * find card in list
- */
-static udiva_card *find_card_in_list(DESCRIPTOR *d)
-{
-       udiva_card *card;
-       struct list_head *tmp;
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card");
-       list_for_each(tmp, &cards) {
-               card = list_entry(tmp, udiva_card, list);
-               if (card->d.request == d->request) {
-                       diva_os_leave_spin_lock(&ll_lock, &old_irql,
-                                               "find card");
-                       return (card);
-               }
-       }
-       diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card");
-       return ((udiva_card *) NULL);
-}
-
-/*
- * new card
- */
-static void um_new_card(DESCRIPTOR *d)
-{
-       int adapter_nr = 0;
-       udiva_card *card = NULL;
-       IDI_SYNC_REQ sync_req;
-       diva_os_spin_lock_magic_t old_irql;
-
-       if (!(card = diva_os_malloc(0, sizeof(udiva_card)))) {
-               DBG_ERR(("cannot get buffer for card"));
-               return;
-       }
-       memcpy(&card->d, d, sizeof(DESCRIPTOR));
-       sync_req.xdi_logical_adapter_number.Req = 0;
-       sync_req.xdi_logical_adapter_number.Rc =
-               IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
-       card->d.request((ENTITY *)&sync_req);
-       adapter_nr =
-               sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
-       card->Id = adapter_nr;
-       if (!(diva_user_mode_idi_create_adapter(d, adapter_nr))) {
-               diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card");
-               list_add_tail(&card->list, &cards);
-               diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card");
-       } else {
-               DBG_ERR(("could not create user mode idi card %d",
-                        adapter_nr));
-               diva_os_free(0, card);
-       }
-}
-
-/*
- * remove card
- */
-static void um_remove_card(DESCRIPTOR *d)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       udiva_card *card = NULL;
-
-       if (!(card = find_card_in_list(d))) {
-               DBG_ERR(("cannot find card to remove"));
-               return;
-       }
-       diva_user_mode_idi_remove_adapter(card->Id);
-       diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card");
-       list_del(&card->list);
-       diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card");
-       DBG_LOG(("idi proc entry removed for card %d", card->Id));
-       diva_os_free(0, card);
-}
-
-/*
- * remove all adapter
- */
-static void __exit remove_all_idi_proc(void)
-{
-       udiva_card *card;
-       diva_os_spin_lock_magic_t old_irql;
-
-rescan:
-       diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove all");
-       if (!list_empty(&cards)) {
-               card = list_entry(cards.next, udiva_card, list);
-               list_del(&card->list);
-               diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
-               diva_user_mode_idi_remove_adapter(card->Id);
-               diva_os_free(0, card);
-               goto rescan;
-       }
-       diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
-}
-
-/*
- * DIDD notify callback
- */
-static void *didd_callback(void *context, DESCRIPTOR *adapter,
-                          int removal)
-{
-       if (adapter->type == IDI_DADAPTER) {
-               DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
-               return (NULL);
-       } else if (adapter->type == IDI_DIMAINT) {
-               if (removal) {
-                       stop_dbg();
-               } else {
-                       memcpy(&MAdapter, adapter, sizeof(MAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
-               }
-       } else if ((adapter->type > 0) && (adapter->type < 16)) {       /* IDI Adapter */
-               if (removal) {
-                       um_remove_card(adapter);
-               } else {
-                       um_new_card(adapter);
-               }
-       }
-       return (NULL);
-}
-
-/*
- * connect DIDD
- */
-static int __init connect_didd(void)
-{
-       int x = 0;
-       int dadapter = 0;
-       IDI_SYNC_REQ req;
-       DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
-
-       DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
-
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
-                       dadapter = 1;
-                       memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       req.didd_notify.e.Req = 0;
-                       req.didd_notify.e.Rc =
-                               IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
-                       req.didd_notify.info.callback = (void *)didd_callback;
-                       req.didd_notify.info.context = NULL;
-                       DAdapter.request((ENTITY *)&req);
-                       if (req.didd_notify.e.Rc != 0xff) {
-                               stop_dbg();
-                               return (0);
-                       }
-                       notify_handle = req.didd_notify.info.handle;
-               } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
-                       memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
-               } else if ((DIDD_Table[x].type > 0)
-                          && (DIDD_Table[x].type < 16)) {      /* IDI Adapter found */
-                       um_new_card(&DIDD_Table[x]);
-               }
-       }
-
-       if (!dadapter) {
-               stop_dbg();
-       }
-
-       return (dadapter);
-}
-
-/*
- *  Disconnect from DIDD
- */
-static void __exit disconnect_didd(void)
-{
-       IDI_SYNC_REQ req;
-
-       stop_dbg();
-
-       req.didd_notify.e.Req = 0;
-       req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
-       req.didd_notify.info.handle = notify_handle;
-       DAdapter.request((ENTITY *)&req);
-}
-
-/*
- * init
- */
-int __init idifunc_init(void)
-{
-       diva_os_initialize_spin_lock(&ll_lock, "idifunc");
-
-       if (diva_user_mode_idi_init()) {
-               DBG_ERR(("init: init failed."));
-               return (0);
-       }
-
-       if (!connect_didd()) {
-               diva_user_mode_idi_finit();
-               DBG_ERR(("init: failed to connect to DIDD."));
-               return (0);
-       }
-       return (1);
-}
-
-/*
- * finit
- */
-void __exit idifunc_finit(void)
-{
-       diva_user_mode_idi_finit();
-       disconnect_didd();
-       remove_all_idi_proc();
-}
diff --git a/drivers/isdn/hardware/eicon/io.c b/drivers/isdn/hardware/eicon/io.c
deleted file mode 100644 (file)
index 8851ce5..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "divasync.h"
-#define MIPS_SCOM
-#include "pkmaint.h" /* pc_main.h, packed in os-dependent fashion */
-#include "di.h"
-#include "mi_pc.h"
-#include "io.h"
-extern ADAPTER *adapter[MAX_ADAPTER];
-extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
-void request(PISDN_ADAPTER, ENTITY *);
-static void pcm_req(PISDN_ADAPTER, ENTITY *);
-/* --------------------------------------------------------------------------
-   local functions
-   -------------------------------------------------------------------------- */
-#define ReqFunc(N)                                                     \
-       static void Request##N(ENTITY *e)                               \
-       { if (IoAdapters[N]) (*IoAdapters[N]->DIRequest)(IoAdapters[N], e); }
-ReqFunc(0)
-ReqFunc(1)
-ReqFunc(2)
-ReqFunc(3)
-ReqFunc(4)
-ReqFunc(5)
-ReqFunc(6)
-ReqFunc(7)
-ReqFunc(8)
-ReqFunc(9)
-ReqFunc(10)
-ReqFunc(11)
-ReqFunc(12)
-ReqFunc(13)
-ReqFunc(14)
-ReqFunc(15)
-IDI_CALL Requests[MAX_ADAPTER] =
-{ &Request0, &Request1, &Request2, &Request3,
-  &Request4, &Request5, &Request6, &Request7,
-  &Request8, &Request9, &Request10, &Request11,
-  &Request12, &Request13, &Request14, &Request15
-};
-/*****************************************************************************/
-/*
-  This array should indicate all new services, that this version of XDI
-  is able to provide to his clients
-*/
-static byte extended_xdi_features[DIVA_XDI_EXTENDED_FEATURES_MAX_SZ + 1] = {
-       (DIVA_XDI_EXTENDED_FEATURES_VALID       |
-        DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR    |
-        DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS    |
-#if defined(DIVA_IDI_RX_DMA)
-        DIVA_XDI_EXTENDED_FEATURE_CMA          |
-        DIVA_XDI_EXTENDED_FEATURE_RX_DMA       |
-        DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA |
-#endif
-        DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC),
-       0
-};
-/*****************************************************************************/
-void
-dump_xlog_buffer(PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc)
-{
-       dword   logLen;
-       word *Xlog   = xlogDesc->buf;
-       word  logCnt = xlogDesc->cnt;
-       word  logOut = xlogDesc->out / sizeof(*Xlog);
-       DBG_FTL(("%s: ************* XLOG recovery (%d) *************",
-                &IoAdapter->Name[0], (int)logCnt))
-               DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
-               for (; logCnt > 0; --logCnt)
-               {
-                       if (!GET_WORD(&Xlog[logOut]))
-                       {
-                               if (--logCnt == 0)
-                                       break;
-                               logOut = 0;
-                       }
-                       if (GET_WORD(&Xlog[logOut]) <= (logOut * sizeof(*Xlog)))
-                       {
-                               if (logCnt > 2)
-                               {
-                                       DBG_FTL(("Possibly corrupted XLOG: %d entries left",
-                                                (int)logCnt))
-                                               }
-                               break;
-                       }
-                       logLen = (dword)(GET_WORD(&Xlog[logOut]) - (logOut * sizeof(*Xlog)));
-                       DBG_FTL_MXLOG(((char *)&Xlog[logOut + 1], (dword)(logLen - 2)))
-                               logOut = (GET_WORD(&Xlog[logOut]) + 1) / sizeof(*Xlog);
-               }
-       DBG_FTL(("%s: ***************** end of XLOG *****************",
-                &IoAdapter->Name[0]))
-               }
-/*****************************************************************************/
-#if defined(XDI_USE_XLOG)
-static char *(ExceptionCauseTable[]) =
-{
-       "Interrupt",
-       "TLB mod /IBOUND",
-       "TLB load /DBOUND",
-       "TLB store",
-       "Address error load",
-       "Address error store",
-       "Instruction load bus error",
-       "Data load/store bus error",
-       "Syscall",
-       "Breakpoint",
-       "Reverd instruction",
-       "Coprocessor unusable",
-       "Overflow",
-       "TRAP",
-       "VCEI",
-       "Floating Point Exception",
-       "CP2",
-       "Reserved 17",
-       "Reserved 18",
-       "Reserved 19",
-       "Reserved 20",
-       "Reserved 21",
-       "Reserved 22",
-       "WATCH",
-       "Reserved 24",
-       "Reserved 25",
-       "Reserved 26",
-       "Reserved 27",
-       "Reserved 28",
-       "Reserved 29",
-       "Reserved 30",
-       "VCED"
-};
-#endif
-void
-dump_trap_frame(PISDN_ADAPTER IoAdapter, byte __iomem *exceptionFrame)
-{
-       MP_XCPTC __iomem *xcept = (MP_XCPTC __iomem *)exceptionFrame;
-       dword    __iomem *regs;
-       regs  = &xcept->regs[0];
-       DBG_FTL(("%s: ***************** CPU TRAPPED *****************",
-                &IoAdapter->Name[0]))
-               DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
-               DBG_FTL(("Cause: %s",
-                        ExceptionCauseTable[(READ_DWORD(&xcept->cr) & 0x0000007c) >> 2]))
-               DBG_FTL(("sr    0x%08x cr    0x%08x epc   0x%08x vaddr 0x%08x",
-                        READ_DWORD(&xcept->sr), READ_DWORD(&xcept->cr),
-                        READ_DWORD(&xcept->epc), READ_DWORD(&xcept->vaddr)))
-               DBG_FTL(("zero  0x%08x at    0x%08x v0    0x%08x v1    0x%08x",
-                        READ_DWORD(&regs[0]), READ_DWORD(&regs[1]),
-                        READ_DWORD(&regs[2]), READ_DWORD(&regs[3])))
-               DBG_FTL(("a0    0x%08x a1    0x%08x a2    0x%08x a3    0x%08x",
-                        READ_DWORD(&regs[4]), READ_DWORD(&regs[5]),
-                        READ_DWORD(&regs[6]), READ_DWORD(&regs[7])))
-               DBG_FTL(("t0    0x%08x t1    0x%08x t2    0x%08x t3    0x%08x",
-                        READ_DWORD(&regs[8]), READ_DWORD(&regs[9]),
-                        READ_DWORD(&regs[10]), READ_DWORD(&regs[11])))
-               DBG_FTL(("t4    0x%08x t5    0x%08x t6    0x%08x t7    0x%08x",
-                        READ_DWORD(&regs[12]), READ_DWORD(&regs[13]),
-                        READ_DWORD(&regs[14]), READ_DWORD(&regs[15])))
-               DBG_FTL(("s0    0x%08x s1    0x%08x s2    0x%08x s3    0x%08x",
-                        READ_DWORD(&regs[16]), READ_DWORD(&regs[17]),
-                        READ_DWORD(&regs[18]), READ_DWORD(&regs[19])))
-               DBG_FTL(("s4    0x%08x s5    0x%08x s6    0x%08x s7    0x%08x",
-                        READ_DWORD(&regs[20]), READ_DWORD(&regs[21]),
-                        READ_DWORD(&regs[22]), READ_DWORD(&regs[23])))
-               DBG_FTL(("t8    0x%08x t9    0x%08x k0    0x%08x k1    0x%08x",
-                        READ_DWORD(&regs[24]), READ_DWORD(&regs[25]),
-                        READ_DWORD(&regs[26]), READ_DWORD(&regs[27])))
-               DBG_FTL(("gp    0x%08x sp    0x%08x s8    0x%08x ra    0x%08x",
-                        READ_DWORD(&regs[28]), READ_DWORD(&regs[29]),
-                        READ_DWORD(&regs[30]), READ_DWORD(&regs[31])))
-               DBG_FTL(("md    0x%08x|%08x         resvd 0x%08x class 0x%08x",
-                        READ_DWORD(&xcept->mdhi), READ_DWORD(&xcept->mdlo),
-                        READ_DWORD(&xcept->reseverd), READ_DWORD(&xcept->xclass)))
-               }
-/* --------------------------------------------------------------------------
-   Real XDI Request function
-   -------------------------------------------------------------------------- */
-void request(PISDN_ADAPTER IoAdapter, ENTITY *e)
-{
-       byte i;
-       diva_os_spin_lock_magic_t irql;
-/*
- * if the Req field in the entity structure is 0,
- * we treat this request as a special function call
- */
-       if (!e->Req)
-       {
-               IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e;
-               switch (e->Rc)
-               {
-#if defined(DIVA_IDI_RX_DMA)
-               case IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION: {
-                       diva_xdi_dma_descriptor_operation_t *pI = \
-                               &syncReq->xdi_dma_descriptor_operation.info;
-                       if (!IoAdapter->dma_map) {
-                               pI->operation         = -1;
-                               pI->descriptor_number = -1;
-                               return;
-                       }
-                       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "dma_op");
-                       if (pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC) {
-                               pI->descriptor_number = diva_alloc_dma_map_entry(\
-                                       (struct _diva_dma_map_entry *)IoAdapter->dma_map);
-                               if (pI->descriptor_number >= 0) {
-                                       dword dma_magic;
-                                       void *local_addr;
-                                       diva_get_dma_map_entry(\
-                                               (struct _diva_dma_map_entry *)IoAdapter->dma_map,
-                                               pI->descriptor_number,
-                                               &local_addr, &dma_magic);
-                                       pI->descriptor_address  = local_addr;
-                                       pI->descriptor_magic    = dma_magic;
-                                       pI->operation           = 0;
-                               } else {
-                                       pI->operation           = -1;
-                               }
-                       } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) &&
-                                  (pI->descriptor_number >= 0)) {
-                               diva_free_dma_map_entry((struct _diva_dma_map_entry *)IoAdapter->dma_map,
-                                                       pI->descriptor_number);
-                               pI->descriptor_number = -1;
-                               pI->operation         = 0;
-                       } else {
-                               pI->descriptor_number = -1;
-                               pI->operation         = -1;
-                       }
-                       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "dma_op");
-               } return;
-#endif
-               case IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER: {
-                       diva_xdi_get_logical_adapter_number_s_t *pI = \
-                               &syncReq->xdi_logical_adapter_number.info;
-                       pI->logical_adapter_number = IoAdapter->ANum;
-                       pI->controller = IoAdapter->ControllerNumber;
-                       pI->total_controllers = IoAdapter->Properties.Adapters;
-               } return;
-               case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: {
-                       diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info;
-                       memset(&prms, 0x00, sizeof(prms));
-                       prms.structure_length = min_t(size_t, sizeof(prms), pI->structure_length);
-                       memset(pI, 0x00, pI->structure_length);
-                       prms.flag_dynamic_l1_down    = (IoAdapter->capi_cfg.cfg_1 & \
-                                                       DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? 1 : 0;
-                       prms.group_optimization_enabled = (IoAdapter->capi_cfg.cfg_1 & \
-                                                          DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON) ? 1 : 0;
-                       memcpy(pI, &prms, prms.structure_length);
-               } return;
-               case IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR:
-                       syncReq->xdi_sdram_bar.info.bar = IoAdapter->sdram_bar;
-                       return;
-               case IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES: {
-                       dword i;
-                       diva_xdi_get_extended_xdi_features_t *pI =\
-                               &syncReq->xdi_extended_features.info;
-                       pI->buffer_length_in_bytes &= ~0x80000000;
-                       if (pI->buffer_length_in_bytes && pI->features) {
-                               memset(pI->features, 0x00, pI->buffer_length_in_bytes);
-                       }
-                       for (i = 0; ((pI->features) && (i < pI->buffer_length_in_bytes) &&
-                                    (i < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ)); i++) {
-                               pI->features[i] = extended_xdi_features[i];
-                       }
-                       if ((pI->buffer_length_in_bytes < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ) ||
-                           (!pI->features)) {
-                               pI->buffer_length_in_bytes =\
-                                       (0x80000000 | DIVA_XDI_EXTENDED_FEATURES_MAX_SZ);
-                       }
-               } return;
-               case IDI_SYNC_REQ_XDI_GET_STREAM:
-                       if (IoAdapter) {
-                               diva_xdi_provide_istream_info(&IoAdapter->a,
-                                                             &syncReq->xdi_stream_info.info);
-                       } else {
-                               syncReq->xdi_stream_info.info.provided_service = 0;
-                       }
-                       return;
-               case IDI_SYNC_REQ_GET_NAME:
-                       if (IoAdapter)
-                       {
-                               strcpy(&syncReq->GetName.name[0], IoAdapter->Name);
-                               DBG_TRC(("xdi: Adapter %d / Name '%s'",
-                                        IoAdapter->ANum, IoAdapter->Name))
-                                       return;
-                       }
-                       syncReq->GetName.name[0] = '\0';
-                       break;
-               case IDI_SYNC_REQ_GET_SERIAL:
-                       if (IoAdapter)
-                       {
-                               syncReq->GetSerial.serial = IoAdapter->serialNo;
-                               DBG_TRC(("xdi: Adapter %d / SerialNo %ld",
-                                        IoAdapter->ANum, IoAdapter->serialNo))
-                                       return;
-                       }
-                       syncReq->GetSerial.serial = 0;
-                       break;
-               case IDI_SYNC_REQ_GET_CARDTYPE:
-                       if (IoAdapter)
-                       {
-                               syncReq->GetCardType.cardtype = IoAdapter->cardType;
-                               DBG_TRC(("xdi: Adapter %d / CardType %ld",
-                                        IoAdapter->ANum, IoAdapter->cardType))
-                                       return;
-                       }
-                       syncReq->GetCardType.cardtype = 0;
-                       break;
-               case IDI_SYNC_REQ_GET_XLOG:
-                       if (IoAdapter)
-                       {
-                               pcm_req(IoAdapter, e);
-                               return;
-                       }
-                       e->Ind = 0;
-                       break;
-               case IDI_SYNC_REQ_GET_DBG_XLOG:
-                       if (IoAdapter)
-                       {
-                               pcm_req(IoAdapter, e);
-                               return;
-                       }
-                       e->Ind = 0;
-                       break;
-               case IDI_SYNC_REQ_GET_FEATURES:
-                       if (IoAdapter)
-                       {
-                               syncReq->GetFeatures.features =
-                                       (unsigned short)IoAdapter->features;
-                               return;
-                       }
-                       syncReq->GetFeatures.features = 0;
-                       break;
-               case IDI_SYNC_REQ_PORTDRV_HOOK:
-                       if (IoAdapter)
-                       {
-                               DBG_TRC(("Xdi:IDI_SYNC_REQ_PORTDRV_HOOK - ignored"))
-                                       return;
-                       }
-                       break;
-               }
-               if (IoAdapter)
-               {
-                       return;
-               }
-       }
-       DBG_TRC(("xdi: Id 0x%x / Req 0x%x / Rc 0x%x", e->Id, e->Req, e->Rc))
-               if (!IoAdapter)
-               {
-                       DBG_FTL(("xdi: uninitialized Adapter used - ignore request"))
-                               return;
-               }
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req");
-/*
- * assign an entity
- */
-       if (!(e->Id & 0x1f))
-       {
-               if (IoAdapter->e_count >= IoAdapter->e_max)
-               {
-                       DBG_FTL(("xdi: all Ids in use (max=%d) --> Req ignored",
-                                IoAdapter->e_max))
-                               diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req");
-                       return;
-               }
-/*
- * find a new free id
- */
-               for (i = 1; IoAdapter->e_tbl[i].e; ++i);
-               IoAdapter->e_tbl[i].e = e;
-               IoAdapter->e_count++;
-               e->No = (byte)i;
-               e->More = 0;
-               e->RCurrent = 0xff;
-       }
-       else
-       {
-               i = e->No;
-       }
-/*
- * if the entity is still busy, ignore the request call
- */
-       if (e->More & XBUSY)
-       {
-               DBG_FTL(("xdi: Id 0x%x busy --> Req 0x%x ignored", e->Id, e->Req))
-                       if (!IoAdapter->trapped && IoAdapter->trapFnc)
-                       {
-                               IoAdapter->trapFnc(IoAdapter);
-                               /*
-                                 Firs trap, also notify user if supported
-                               */
-                               if (IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
-                                       (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
-                               }
-                       }
-               diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req");
-               return;
-       }
-/*
- * initialize transmit status variables
- */
-       e->More |= XBUSY;
-       e->More &= ~XMOREF;
-       e->XCurrent = 0;
-       e->XOffset = 0;
-/*
- * queue this entity in the adapter request queue
- */
-       IoAdapter->e_tbl[i].next = 0;
-       if (IoAdapter->head)
-       {
-               IoAdapter->e_tbl[IoAdapter->tail].next = i;
-               IoAdapter->tail = i;
-       }
-       else
-       {
-               IoAdapter->head = i;
-               IoAdapter->tail = i;
-       }
-/*
- * queue the DPC to process the request
- */
-       diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr);
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req");
-}
-/* ---------------------------------------------------------------------
-   Main DPC routine
-   --------------------------------------------------------------------- */
-void DIDpcRoutine(struct _diva_os_soft_isr *psoft_isr, void *Context) {
-       PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)Context;
-       ADAPTER *a = &IoAdapter->a;
-       diva_os_atomic_t *pin_dpc = &IoAdapter->in_dpc;
-       if (diva_os_atomic_increment(pin_dpc) == 1) {
-               do {
-                       if (IoAdapter->tst_irq(a))
-                       {
-                               if (!IoAdapter->Unavailable)
-                                       IoAdapter->dpc(a);
-                               IoAdapter->clr_irq(a);
-                       }
-                       IoAdapter->out(a);
-               } while (diva_os_atomic_decrement(pin_dpc) > 0);
-               /* ----------------------------------------------------------------
-                  Look for XLOG request (cards with indirect addressing)
-                  ---------------------------------------------------------------- */
-               if (IoAdapter->pcm_pending) {
-                       struct pc_maint *pcm;
-                       diva_os_spin_lock_magic_t OldIrql;
-                       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                                               &OldIrql,
-                                               "data_dpc");
-                       pcm = (struct pc_maint *)IoAdapter->pcm_data;
-                       switch (IoAdapter->pcm_pending) {
-                       case 1: /* ask card for XLOG */
-                               a->ram_out(a, &IoAdapter->pcm->rc, 0);
-                               a->ram_out(a, &IoAdapter->pcm->req, pcm->req);
-                               IoAdapter->pcm_pending = 2;
-                               break;
-                       case 2: /* Try to get XLOG from the card */
-                               if ((int)(a->ram_in(a, &IoAdapter->pcm->rc))) {
-                                       a->ram_in_buffer(a, IoAdapter->pcm, pcm, sizeof(*pcm));
-                                       IoAdapter->pcm_pending = 3;
-                               }
-                               break;
-                       case 3: /* let XDI recovery XLOG */
-                               break;
-                       }
-                       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                                               &OldIrql,
-                                               "data_dpc");
-               }
-               /* ---------------------------------------------------------------- */
-       }
-}
-/* --------------------------------------------------------------------------
-   XLOG interface
-   -------------------------------------------------------------------------- */
-static void
-pcm_req(PISDN_ADAPTER IoAdapter, ENTITY *e)
-{
-       diva_os_spin_lock_magic_t OldIrql;
-       int              i, rc;
-       ADAPTER         *a = &IoAdapter->a;
-       struct pc_maint *pcm = (struct pc_maint *)&e->Ind;
-/*
- * special handling of I/O based card interface
- * the memory access isn't an atomic operation !
- */
-       if (IoAdapter->Properties.Card == CARD_MAE)
-       {
-               diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                                       &OldIrql,
-                                       "data_pcm_1");
-               IoAdapter->pcm_data = (void *)pcm;
-               IoAdapter->pcm_pending = 1;
-               diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr);
-               diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                                       &OldIrql,
-                                       "data_pcm_1");
-               for (rc = 0, i = (IoAdapter->trapped ? 3000 : 250); !rc && (i > 0); --i)
-               {
-                       diva_os_sleep(1);
-                       if (IoAdapter->pcm_pending == 3) {
-                               diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                                                       &OldIrql,
-                                                       "data_pcm_3");
-                               IoAdapter->pcm_pending = 0;
-                               IoAdapter->pcm_data    = NULL;
-                               diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                                                       &OldIrql,
-                                                       "data_pcm_3");
-                               return;
-                       }
-                       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                                               &OldIrql,
-                                               "data_pcm_2");
-                       diva_os_schedule_soft_isr(&IoAdapter->req_soft_isr);
-                       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                                               &OldIrql,
-                                               "data_pcm_2");
-               }
-               diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                                       &OldIrql,
-                                       "data_pcm_4");
-               IoAdapter->pcm_pending = 0;
-               IoAdapter->pcm_data    = NULL;
-               diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                                       &OldIrql,
-                                       "data_pcm_4");
-               goto Trapped;
-       }
-/*
- * memory based shared ram is accessible from different
- * processors without disturbing concurrent processes.
- */
-       a->ram_out(a, &IoAdapter->pcm->rc, 0);
-       a->ram_out(a, &IoAdapter->pcm->req, pcm->req);
-       for (i = (IoAdapter->trapped ? 3000 : 250); --i > 0;)
-       {
-               diva_os_sleep(1);
-               rc = (int)(a->ram_in(a, &IoAdapter->pcm->rc));
-               if (rc)
-               {
-                       a->ram_in_buffer(a, IoAdapter->pcm, pcm, sizeof(*pcm));
-                       return;
-               }
-       }
-Trapped:
-       if (IoAdapter->trapFnc)
-       {
-               int trapped = IoAdapter->trapped;
-               IoAdapter->trapFnc(IoAdapter);
-               /*
-                 Firs trap, also notify user if supported
-               */
-               if (!trapped && IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
-                       (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
-               }
-       }
-}
-/*------------------------------------------------------------------*/
-/* ram access functions for memory mapped cards                     */
-/*------------------------------------------------------------------*/
-byte mem_in(ADAPTER *a, void *addr)
-{
-       byte val;
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       val = READ_BYTE(Base + (unsigned long)addr);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-       return (val);
-}
-word mem_inw(ADAPTER *a, void *addr)
-{
-       word val;
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       val = READ_WORD((Base + (unsigned long)addr));
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-       return (val);
-}
-void mem_in_dw(ADAPTER *a, void *addr, dword *data, int dwords)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       while (dwords--) {
-               *data++ = READ_DWORD((Base + (unsigned long)addr));
-               addr += 4;
-       }
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_in_buffer(ADAPTER *a, void *addr, void *buffer, word length)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       memcpy_fromio(buffer, (Base + (unsigned long)addr), length);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
-{
-       PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io;
-       IoAdapter->RBuffer.length = mem_inw(a, &RBuffer->length);
-       mem_in_buffer(a, RBuffer->P, IoAdapter->RBuffer.P,
-                     IoAdapter->RBuffer.length);
-       e->RBuffer = (DBUFFER *)&IoAdapter->RBuffer;
-}
-void mem_out(ADAPTER *a, void *addr, byte data)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       WRITE_BYTE(Base + (unsigned long)addr, data);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_outw(ADAPTER *a, void *addr, word data)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       WRITE_WORD((Base + (unsigned long)addr), data);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_out_dw(ADAPTER *a, void *addr, const dword *data, int dwords)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       while (dwords--) {
-               WRITE_DWORD((Base + (unsigned long)addr), *data);
-               addr += 4;
-               data++;
-       }
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_out_buffer(ADAPTER *a, void *addr, void *buffer, word length)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       memcpy_toio((Base + (unsigned long)addr), buffer, length);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-void mem_inc(ADAPTER *a, void *addr)
-{
-       volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
-       byte  x = READ_BYTE(Base + (unsigned long)addr);
-       WRITE_BYTE(Base + (unsigned long)addr, x + 1);
-       DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
-}
-/*------------------------------------------------------------------*/
-/* ram access functions for io-mapped cards                         */
-/*------------------------------------------------------------------*/
-byte io_in(ADAPTER *a, void *adr)
-{
-       byte val;
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       val = inpp(Port);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-       return (val);
-}
-word io_inw(ADAPTER *a, void *adr)
-{
-       word val;
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       val = inppw(Port);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-       return (val);
-}
-void io_in_buffer(ADAPTER *a, void *adr, void *buffer, word len)
-{
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       byte *P = (byte *)buffer;
-       if ((long)adr & 1) {
-               outppw(Port + 4, (word)(unsigned long)adr);
-               *P = inpp(Port);
-               P++;
-               adr = ((byte *) adr) + 1;
-               len--;
-               if (!len) {
-                       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-                       return;
-               }
-       }
-       outppw(Port + 4, (word)(unsigned long)adr);
-       inppw_buffer(Port, P, len + 1);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
-{
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)RBuffer);
-       ((PISDN_ADAPTER)a->io)->RBuffer.length = inppw(Port);
-       inppw_buffer(Port, ((PISDN_ADAPTER)a->io)->RBuffer.P, ((PISDN_ADAPTER)a->io)->RBuffer.length + 1);
-       e->RBuffer = (DBUFFER *) &(((PISDN_ADAPTER)a->io)->RBuffer);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-void io_out(ADAPTER *a, void *adr, byte data)
-{
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       outpp(Port, data);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-void io_outw(ADAPTER *a, void *adr, word data)
-{
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       outppw(Port, data);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-void io_out_buffer(ADAPTER *a, void *adr, void *buffer, word len)
-{
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       byte *P = (byte *)buffer;
-       if ((long)adr & 1) {
-               outppw(Port + 4, (word)(unsigned long)adr);
-               outpp(Port, *P);
-               P++;
-               adr = ((byte *) adr) + 1;
-               len--;
-               if (!len) {
-                       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-                       return;
-               }
-       }
-       outppw(Port + 4, (word)(unsigned long)adr);
-       outppw_buffer(Port, P, len + 1);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-void io_inc(ADAPTER *a, void *adr)
-{
-       byte x;
-       byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       x = inpp(Port);
-       outppw(Port + 4, (word)(unsigned long)adr);
-       outpp(Port, x + 1);
-       DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
-}
-/*------------------------------------------------------------------*/
-/* OS specific functions related to queuing of entities             */
-/*------------------------------------------------------------------*/
-void free_entity(ADAPTER *a, byte e_no)
-{
-       PISDN_ADAPTER IoAdapter;
-       diva_os_spin_lock_magic_t irql;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_free");
-       IoAdapter->e_tbl[e_no].e = NULL;
-       IoAdapter->e_count--;
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_free");
-}
-void assign_queue(ADAPTER *a, byte e_no, word ref)
-{
-       PISDN_ADAPTER IoAdapter;
-       diva_os_spin_lock_magic_t irql;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_assign");
-       IoAdapter->e_tbl[e_no].assign_ref = ref;
-       IoAdapter->e_tbl[e_no].next = (byte)IoAdapter->assign;
-       IoAdapter->assign = e_no;
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_assign");
-}
-byte get_assign(ADAPTER *a, word ref)
-{
-       PISDN_ADAPTER IoAdapter;
-       diva_os_spin_lock_magic_t irql;
-       byte e_no;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock,
-                               &irql,
-                               "data_assign_get");
-       for (e_no = (byte)IoAdapter->assign;
-           e_no && IoAdapter->e_tbl[e_no].assign_ref != ref;
-           e_no = IoAdapter->e_tbl[e_no].next);
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock,
-                               &irql,
-                               "data_assign_get");
-       return e_no;
-}
-void req_queue(ADAPTER *a, byte e_no)
-{
-       PISDN_ADAPTER IoAdapter;
-       diva_os_spin_lock_magic_t irql;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_q");
-       IoAdapter->e_tbl[e_no].next = 0;
-       if (IoAdapter->head) {
-               IoAdapter->e_tbl[IoAdapter->tail].next = e_no;
-               IoAdapter->tail = e_no;
-       }
-       else {
-               IoAdapter->head = e_no;
-               IoAdapter->tail = e_no;
-       }
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_q");
-}
-byte look_req(ADAPTER *a)
-{
-       PISDN_ADAPTER IoAdapter;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       return ((byte)IoAdapter->head);
-}
-void next_req(ADAPTER *a)
-{
-       PISDN_ADAPTER IoAdapter;
-       diva_os_spin_lock_magic_t irql;
-       IoAdapter = (PISDN_ADAPTER) a->io;
-       diva_os_enter_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_next");
-       IoAdapter->head = IoAdapter->e_tbl[IoAdapter->head].next;
-       if (!IoAdapter->head) IoAdapter->tail = 0;
-       diva_os_leave_spin_lock(&IoAdapter->data_spin_lock, &irql, "data_req_next");
-}
-/*------------------------------------------------------------------*/
-/* memory map functions                                             */
-/*------------------------------------------------------------------*/
-ENTITY *entity_ptr(ADAPTER *a, byte e_no)
-{
-       PISDN_ADAPTER IoAdapter;
-       IoAdapter = (PISDN_ADAPTER)a->io;
-       return (IoAdapter->e_tbl[e_no].e);
-}
-void *PTR_X(ADAPTER *a, ENTITY *e)
-{
-       return ((void *) e->X);
-}
-void *PTR_R(ADAPTER *a, ENTITY *e)
-{
-       return ((void *) e->R);
-}
-void *PTR_P(ADAPTER *a, ENTITY *e, void *P)
-{
-       return P;
-}
-void CALLBACK(ADAPTER *a, ENTITY *e)
-{
-       if (e && e->callback)
-               e->callback(e);
-}
diff --git a/drivers/isdn/hardware/eicon/io.h b/drivers/isdn/hardware/eicon/io.h
deleted file mode 100644 (file)
index 01deced..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_COMMON_IO_H_INC__ /* { */
-#define __DIVA_XDI_COMMON_IO_H_INC__
-/*
-  maximum = 16 adapters
-*/
-#define DI_MAX_LINKS    MAX_ADAPTER
-#define ISDN_MAX_NUM_LEN 60
-/* --------------------------------------------------------------------------
-   structure for quadro card management (obsolete for
-   systems that do provide per card load event)
-   -------------------------------------------------------------------------- */
-typedef struct {
-       dword         Num;
-       DEVICE_NAME   DeviceName[4];
-       PISDN_ADAPTER QuadroAdapter[4];
-} ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY;
-/* --------------------------------------------------------------------------
-   Special OS memory support structures
-   -------------------------------------------------------------------------- */
-#define MAX_MAPPED_ENTRIES 8
-typedef struct {
-       void *Address;
-       dword    Length;
-} ADAPTER_MEMORY;
-/* --------------------------------------------------------------------------
-   Configuration of XDI clients carried by XDI
-   -------------------------------------------------------------------------- */
-#define DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON      0x01
-#define DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON 0x02
-typedef struct _diva_xdi_capi_cfg {
-       byte cfg_1;
-} diva_xdi_capi_cfg_t;
-/* --------------------------------------------------------------------------
-   Main data structure kept per adapter
-   -------------------------------------------------------------------------- */
-struct _ISDN_ADAPTER {
-       void (*DIRequest)(PISDN_ADAPTER, ENTITY *);
-       int State; /* from NT4 1.srv, a good idea, but  a poor achievement */
-       int Initialized;
-       int RegisteredWithDidd;
-       int Unavailable;  /* callback function possible? */
-       int ResourcesClaimed;
-       int PnpBiosConfigUsed;
-       dword Logging;
-       dword features;
-       char ProtocolIdString[80];
-       /*
-         remember mapped memory areas
-       */
-       ADAPTER_MEMORY MappedMemory[MAX_MAPPED_ENTRIES];
-       CARD_PROPERTIES Properties;
-       dword cardType;
-       dword protocol_id;       /* configured protocol identifier */
-       char protocol_name[8];  /* readable name of protocol */
-       dword BusType;
-       dword BusNumber;
-       dword slotNumber;
-       dword slotId;
-       dword ControllerNumber;  /* for QUADRO cards only */
-       PISDN_ADAPTER MultiMaster;       /* for 4-BRI card only - use MultiMaster or QuadroList */
-       PADAPTER_LIST_ENTRY QuadroList;        /* for QUADRO card  only */
-       PDEVICE_OBJECT DeviceObject;
-       dword DeviceId;
-       diva_os_adapter_irq_info_t irq_info;
-       dword volatile IrqCount;
-       int trapped;
-       dword DspCodeBaseAddr;
-       dword MaxDspCodeSize;
-       dword downloadAddr;
-       dword DspCodeBaseAddrTable[4]; /* add. for MultiMaster */
-       dword MaxDspCodeSizeTable[4]; /* add. for MultiMaster */
-       dword downloadAddrTable[4]; /* add. for MultiMaster */
-       dword MemoryBase;
-       dword MemorySize;
-       byte __iomem *Address;
-       byte __iomem *Config;
-       byte __iomem *Control;
-       byte __iomem *reset;
-       byte __iomem *port;
-       byte __iomem *ram;
-       byte __iomem *cfg;
-       byte __iomem *prom;
-       byte __iomem *ctlReg;
-       struct pc_maint  *pcm;
-       diva_os_dependent_devica_name_t os_name;
-       byte Name[32];
-       dword serialNo;
-       dword ANum;
-       dword ArchiveType; /* ARCHIVE_TYPE_NONE ..._SINGLE ..._USGEN ..._MULTI */
-       char *ProtocolSuffix; /* internal protocolfile table */
-       char Archive[32];
-       char Protocol[32];
-       char AddDownload[32]; /* Dsp- or other additional download files */
-       char Oad1[ISDN_MAX_NUM_LEN];
-       char Osa1[ISDN_MAX_NUM_LEN];
-       char Oad2[ISDN_MAX_NUM_LEN];
-       char Osa2[ISDN_MAX_NUM_LEN];
-       char Spid1[ISDN_MAX_NUM_LEN];
-       char Spid2[ISDN_MAX_NUM_LEN];
-       byte nosig;
-       byte BriLayer2LinkCount; /* amount of TEI's that adapter will support in P2MP mode */
-       dword Channels;
-       dword tei;
-       dword nt2;
-       dword TerminalCount;
-       dword WatchDog;
-       dword Permanent;
-       dword BChMask; /* B channel mask for unchannelized modes */
-       dword StableL2;
-       dword DidLen;
-       dword NoOrderCheck;
-       dword ForceLaw; /* VoiceCoding - default:0, a-law: 1, my-law: 2 */
-       dword SigFlags;
-       dword LowChannel;
-       dword NoHscx30;
-       dword ProtVersion;
-       dword crc4;
-       dword L1TristateOrQsig; /* enable Layer 1 Tristate (bit 2)Or Qsig params (bit 0,1)*/
-       dword InitialDspInfo;
-       dword ModemGuardTone;
-       dword ModemMinSpeed;
-       dword ModemMaxSpeed;
-       dword ModemOptions;
-       dword ModemOptions2;
-       dword ModemNegotiationMode;
-       dword ModemModulationsMask;
-       dword ModemTransmitLevel;
-       dword FaxOptions;
-       dword FaxMaxSpeed;
-       dword Part68LevelLimiter;
-       dword UsEktsNumCallApp;
-       byte UsEktsFeatAddConf;
-       byte UsEktsFeatRemoveConf;
-       byte UsEktsFeatCallTransfer;
-       byte UsEktsFeatMsgWaiting;
-       byte QsigDialect;
-       byte ForceVoiceMailAlert;
-       byte DisableAutoSpid;
-       byte ModemCarrierWaitTimeSec;
-       byte ModemCarrierLossWaitTimeTenthSec;
-       byte PiafsLinkTurnaroundInFrames;
-       byte DiscAfterProgress;
-       byte AniDniLimiter[3];
-       byte TxAttenuation;  /* PRI/E1 only: attenuate TX signal */
-       word QsigFeatures;
-       dword GenerateRingtone;
-       dword SupplementaryServicesFeatures;
-       dword R2Dialect;
-       dword R2CasOptions;
-       dword FaxV34Options;
-       dword DisabledDspMask;
-       dword AdapterTestMask;
-       dword DspImageLength;
-       word AlertToIn20mSecTicks;
-       word ModemEyeSetup;
-       byte R2CtryLength;
-       byte CCBSRelTimer;
-       byte *PcCfgBufferFile;/* flexible parameter via file */
-       byte *PcCfgBuffer; /* flexible parameter via multistring */
-       diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */
-       diva_os_board_trace_t board_trace; /* traces from the board */
-       diva_os_spin_lock_t isr_spin_lock;
-       diva_os_spin_lock_t data_spin_lock;
-       diva_os_soft_isr_t req_soft_isr;
-       diva_os_soft_isr_t isr_soft_isr;
-       diva_os_atomic_t  in_dpc;
-       PBUFFER RBuffer;        /* Copy of receive lookahead buffer */
-       word e_max;
-       word e_count;
-       E_INFO *e_tbl;
-       word assign;         /* list of pending ASSIGNs  */
-       word head;           /* head of request queue    */
-       word tail;           /* tail of request queue    */
-       ADAPTER a;             /* not a separate structure */
-       void (*out)(ADAPTER *a);
-       byte (*dpc)(ADAPTER *a);
-       byte (*tst_irq)(ADAPTER *a);
-       void (*clr_irq)(ADAPTER *a);
-       int (*load)(PISDN_ADAPTER);
-       int (*mapmem)(PISDN_ADAPTER);
-       int (*chkIrq)(PISDN_ADAPTER);
-       void (*disIrq)(PISDN_ADAPTER);
-       void (*start)(PISDN_ADAPTER);
-       void (*stop)(PISDN_ADAPTER);
-       void (*rstFnc)(PISDN_ADAPTER);
-       void (*trapFnc)(PISDN_ADAPTER);
-       dword (*DetectDsps)(PISDN_ADAPTER);
-       void (*os_trap_nfy_Fnc)(PISDN_ADAPTER, dword);
-       diva_os_isr_callback_t diva_isr_handler;
-       dword sdram_bar;  /* must be 32 bit */
-       dword fpga_features;
-       volatile int pcm_pending;
-       volatile void *pcm_data;
-       diva_xdi_capi_cfg_t capi_cfg;
-       dword tasks;
-       void *dma_map;
-       int (*DivaAdapterTestProc)(PISDN_ADAPTER);
-       void *AdapterTestMemoryStart;
-       dword AdapterTestMemoryLength;
-       const byte *cfg_lib_memory_init;
-       dword cfg_lib_memory_init_length;
-};
-/* ---------------------------------------------------------------------
-   Entity table
-   --------------------------------------------------------------------- */
-struct e_info_s {
-       ENTITY *e;
-       byte          next;                   /* chaining index           */
-       word          assign_ref;             /* assign reference         */
-};
-/* ---------------------------------------------------------------------
-   S-cards shared ram structure for loading
-   --------------------------------------------------------------------- */
-struct s_load {
-       byte ctrl;
-       byte card;
-       byte msize;
-       byte fill0;
-       word ebit;
-       word elocl;
-       word eloch;
-       byte reserved[20];
-       word signature;
-       byte fill[224];
-       byte b[256];
-};
-#define PR_RAM  ((struct pr_ram *)0)
-#define RAM ((struct dual *)0)
-/* ---------------------------------------------------------------------
-   platform specific conversions
-   --------------------------------------------------------------------- */
-extern void *PTR_P(ADAPTER *a, ENTITY *e, void *P);
-extern void *PTR_X(ADAPTER *a, ENTITY *e);
-extern void *PTR_R(ADAPTER *a, ENTITY *e);
-extern void CALLBACK(ADAPTER *a, ENTITY *e);
-extern void set_ram(void **adr_ptr);
-/* ---------------------------------------------------------------------
-   ram access functions for io mapped cards
-   --------------------------------------------------------------------- */
-byte io_in(ADAPTER *a, void *adr);
-word io_inw(ADAPTER *a, void *adr);
-void io_in_buffer(ADAPTER *a, void *adr, void *P, word length);
-void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
-void io_out(ADAPTER *a, void *adr, byte data);
-void io_outw(ADAPTER *a, void *adr, word data);
-void io_out_buffer(ADAPTER *a, void *adr, void *P, word length);
-void io_inc(ADAPTER *a, void *adr);
-void bri_in_buffer(PISDN_ADAPTER IoAdapter, dword Pos,
-                  void *Buf, dword Len);
-int bri_out_buffer(PISDN_ADAPTER IoAdapter, dword Pos,
-                  void *Buf, dword Len, int Verify);
-/* ---------------------------------------------------------------------
-   ram access functions for memory mapped cards
-   --------------------------------------------------------------------- */
-byte mem_in(ADAPTER *a, void *adr);
-word mem_inw(ADAPTER *a, void *adr);
-void mem_in_buffer(ADAPTER *a, void *adr, void *P, word length);
-void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
-void mem_out(ADAPTER *a, void *adr, byte data);
-void mem_outw(ADAPTER *a, void *adr, word data);
-void mem_out_buffer(ADAPTER *a, void *adr, void *P, word length);
-void mem_inc(ADAPTER *a, void *adr);
-void mem_in_dw(ADAPTER *a, void *addr, dword *data, int dwords);
-void mem_out_dw(ADAPTER *a, void *addr, const dword *data, int dwords);
-/* ---------------------------------------------------------------------
-   functions exported by io.c
-   --------------------------------------------------------------------- */
-extern IDI_CALL Requests[MAX_ADAPTER];
-extern void     DIDpcRoutine(struct _diva_os_soft_isr *psoft_isr,
-                            void *context);
-extern void     request(PISDN_ADAPTER, ENTITY *);
-/* ---------------------------------------------------------------------
-   trapFn helpers, used to recover debug trace from dead card
-   --------------------------------------------------------------------- */
-typedef struct {
-       word *buf;
-       word  cnt;
-       word  out;
-} Xdesc;
-extern void dump_trap_frame(PISDN_ADAPTER IoAdapter, byte __iomem *exception);
-extern void dump_xlog_buffer(PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc);
-/* --------------------------------------------------------------------- */
-#endif  /* } __DIVA_XDI_COMMON_IO_H_INC__ */
diff --git a/drivers/isdn/hardware/eicon/istream.c b/drivers/isdn/hardware/eicon/istream.c
deleted file mode 100644 (file)
index 045bda5..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#if defined(DIVA_ISTREAM) /* { */
-#include "pc.h"
-#include "pr_pc.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "di.h"
-#if !defined USE_EXTENDED_DEBUGS
-#include "dimaint.h"
-#else
-#define dprintf
-#endif
-#include "dfifo.h"
-int diva_istream_write(void *context,
-                      int   Id,
-                      void *data,
-                      int length,
-                      int final,
-                      byte usr1,
-                      byte usr2);
-int diva_istream_read(void *context,
-                     int Id,
-                     void *data,
-                     int max_length,
-                     int *final,
-                     byte *usr1,
-                     byte *usr2);
-/* -------------------------------------------------------------------
-   Does provide iStream interface to the client
-   ------------------------------------------------------------------- */
-void diva_xdi_provide_istream_info(ADAPTER *a,
-                                  diva_xdi_stream_interface_t *pi) {
-       pi->provided_service = 0;
-}
-/* ------------------------------------------------------------------
-   Does write the data from caller's buffer to the card's
-   stream interface.
-   If synchronous service was requested, then function
-   does return amount of data written to stream.
-   'final' does indicate that piece of data to be written is
-   final part of frame (necessary only by structured datatransfer)
-   return  0 if zero lengh packet was written
-   return -1 if stream is full
-   ------------------------------------------------------------------ */
-int diva_istream_write(void *context,
-                      int Id,
-                      void *data,
-                      int length,
-                      int final,
-                      byte usr1,
-                      byte usr2) {
-       ADAPTER *a = (ADAPTER *)context;
-       int written = 0, to_write = -1;
-       char tmp[4];
-       byte *data_ptr = (byte *)data;
-       for (;;) {
-               a->ram_in_dw(a,
-#ifdef PLATFORM_GT_32BIT
-                             ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
-#else
-                             (void *)(a->tx_stream[Id] + a->tx_pos[Id]),
-#endif
-                             (dword *)&tmp[0],
-                             1);
-               if (tmp[0] & DIVA_DFIFO_READY) { /* No free blocks more */
-                       if (to_write < 0)
-                               return (-1); /* was not able to write       */
-                       break;     /* only part of message was written */
-               }
-               to_write = min(length, DIVA_DFIFO_DATA_SZ);
-               if (to_write) {
-                       a->ram_out_buffer(a,
-#ifdef PLATFORM_GT_32BIT
-                                          ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id] + 4),
-#else
-                                          (void *)(a->tx_stream[Id] + a->tx_pos[Id] + 4),
-#endif
-                                          data_ptr,
-                                          (word)to_write);
-                       length  -= to_write;
-                       written  += to_write;
-                       data_ptr += to_write;
-               }
-               tmp[1] = (char)to_write;
-               tmp[0] = (tmp[0] & DIVA_DFIFO_WRAP) |
-                       DIVA_DFIFO_READY |
-                       ((!length && final) ? DIVA_DFIFO_LAST : 0);
-               if (tmp[0] & DIVA_DFIFO_LAST) {
-                       tmp[2] = usr1;
-                       tmp[3] = usr2;
-               }
-               a->ram_out_dw(a,
-#ifdef PLATFORM_GT_32BIT
-                              ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
-#else
-                              (void *)(a->tx_stream[Id] + a->tx_pos[Id]),
-#endif
-                              (dword *)&tmp[0],
-                              1);
-               if (tmp[0] & DIVA_DFIFO_WRAP) {
-                       a->tx_pos[Id]  = 0;
-               } else {
-                       a->tx_pos[Id] += DIVA_DFIFO_STEP;
-               }
-               if (!length) {
-                       break;
-               }
-       }
-       return (written);
-}
-/* -------------------------------------------------------------------
-   In case of SYNCRONOUS service:
-   Does write data from stream in caller's buffer.
-   Does return amount of data written to buffer
-   Final flag is set on return if last part of structured frame
-   was received
-   return 0  if zero packet was received
-   return -1 if stream is empty
-   return -2 if read buffer does not profide sufficient space
-   to accommodate entire segment
-   max_length should be at least 68 bytes
-   ------------------------------------------------------------------- */
-int diva_istream_read(void *context,
-                     int Id,
-                     void *data,
-                     int max_length,
-                     int *final,
-                     byte *usr1,
-                     byte *usr2) {
-       ADAPTER *a = (ADAPTER *)context;
-       int read = 0, to_read = -1;
-       char tmp[4];
-       byte *data_ptr = (byte *)data;
-       *final = 0;
-       for (;;) {
-               a->ram_in_dw(a,
-#ifdef PLATFORM_GT_32BIT
-                             ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
-#else
-                             (void *)(a->rx_stream[Id] + a->rx_pos[Id]),
-#endif
-                             (dword *)&tmp[0],
-                             1);
-               if (tmp[1] > max_length) {
-                       if (to_read < 0)
-                               return (-2); /* was not able to read */
-                       break;
-               }
-               if (!(tmp[0] & DIVA_DFIFO_READY)) {
-                       if (to_read < 0)
-                               return (-1); /* was not able to read */
-                       break;
-               }
-               to_read = min(max_length, (int)tmp[1]);
-               if (to_read) {
-                       a->ram_in_buffer(a,
-#ifdef PLATFORM_GT_32BIT
-                                        ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id] + 4),
-#else
-                                        (void *)(a->rx_stream[Id] + a->rx_pos[Id] + 4),
-#endif
-                                        data_ptr,
-                                        (word)to_read);
-                       max_length -= to_read;
-                       read     += to_read;
-                       data_ptr  += to_read;
-               }
-               if (tmp[0] & DIVA_DFIFO_LAST) {
-                       *final = 1;
-               }
-               tmp[0] &= DIVA_DFIFO_WRAP;
-               a->ram_out_dw(a,
-#ifdef PLATFORM_GT_32BIT
-                             ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
-#else
-                             (void *)(a->rx_stream[Id] + a->rx_pos[Id]),
-#endif
-                             (dword *)&tmp[0],
-                             1);
-               if (tmp[0] & DIVA_DFIFO_WRAP) {
-                       a->rx_pos[Id]  = 0;
-               } else {
-                       a->rx_pos[Id] += DIVA_DFIFO_STEP;
-               }
-               if (*final) {
-                       if (usr1)
-                               *usr1 = tmp[2];
-                       if (usr2)
-                               *usr2 = tmp[3];
-                       break;
-               }
-       }
-       return (read);
-}
-/* ---------------------------------------------------------------------
-   Does check if one of streams had caused interrupt and does
-   wake up corresponding application
-   --------------------------------------------------------------------- */
-void pr_stream(ADAPTER *a) {
-}
-#endif /* } */
diff --git a/drivers/isdn/hardware/eicon/kst_ifc.h b/drivers/isdn/hardware/eicon/kst_ifc.h
deleted file mode 100644 (file)
index 894fdfd..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2000.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    1.9
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_EICON_TRACE_API__
-#define __DIVA_EICON_TRACE_API__
-
-#define DIVA_TRACE_LINE_TYPE_LEN 64
-#define DIVA_TRACE_IE_LEN        64
-#define DIVA_TRACE_FAX_PRMS_LEN  128
-
-typedef struct _diva_trace_ie {
-       byte length;
-       byte data[DIVA_TRACE_IE_LEN];
-} diva_trace_ie_t;
-
-/*
-  Structure used to represent "State\\BX\\Modem" directory
-  to user.
-*/
-typedef struct _diva_trace_modem_state {
-       dword   ChannelNumber;
-
-       dword   Event;
-
-       dword   Norm;
-
-       dword Options; /* Options received from Application */
-
-       dword   TxSpeed;
-       dword   RxSpeed;
-
-       dword RoundtripMsec;
-
-       dword SymbolRate;
-
-       int             RxLeveldBm;
-       int             EchoLeveldBm;
-
-       dword   SNRdb;
-       dword MAE;
-
-       dword LocalRetrains;
-       dword RemoteRetrains;
-       dword LocalResyncs;
-       dword RemoteResyncs;
-
-       dword DiscReason;
-
-} diva_trace_modem_state_t;
-
-/*
-  Representation of "State\\BX\\FAX" directory
-*/
-typedef struct _diva_trace_fax_state {
-       dword   ChannelNumber;
-       dword Event;
-       dword Page_Counter;
-       dword Features;
-       char Station_ID[DIVA_TRACE_FAX_PRMS_LEN];
-       char Subaddress[DIVA_TRACE_FAX_PRMS_LEN];
-       char Password[DIVA_TRACE_FAX_PRMS_LEN];
-       dword Speed;
-       dword Resolution;
-       dword Paper_Width;
-       dword Paper_Length;
-       dword Scanline_Time;
-       dword Disc_Reason;
-       dword   dummy;
-} diva_trace_fax_state_t;
-
-/*
-  Structure used to represent Interface State in the abstract
-  and interface/D-channel protocol independent form.
-*/
-typedef struct _diva_trace_interface_state {
-       char Layer1[DIVA_TRACE_LINE_TYPE_LEN];
-       char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
-} diva_trace_interface_state_t;
-
-typedef struct _diva_incoming_call_statistics {
-       dword Calls;
-       dword Connected;
-       dword User_Busy;
-       dword Call_Rejected;
-       dword Wrong_Number;
-       dword Incompatible_Dst;
-       dword Out_of_Order;
-       dword Ignored;
-} diva_incoming_call_statistics_t;
-
-typedef struct _diva_outgoing_call_statistics {
-       dword Calls;
-       dword Connected;
-       dword User_Busy;
-       dword No_Answer;
-       dword Wrong_Number;
-       dword Call_Rejected;
-       dword Other_Failures;
-} diva_outgoing_call_statistics_t;
-
-typedef struct _diva_modem_call_statistics {
-       dword Disc_Normal;
-       dword Disc_Unspecified;
-       dword Disc_Busy_Tone;
-       dword Disc_Congestion;
-       dword Disc_Carr_Wait;
-       dword Disc_Trn_Timeout;
-       dword Disc_Incompat;
-       dword Disc_Frame_Rej;
-       dword Disc_V42bis;
-} diva_modem_call_statistics_t;
-
-typedef struct _diva_fax_call_statistics {
-       dword Disc_Normal;
-       dword Disc_Not_Ident;
-       dword Disc_No_Response;
-       dword Disc_Retries;
-       dword Disc_Unexp_Msg;
-       dword Disc_No_Polling;
-       dword Disc_Training;
-       dword Disc_Unexpected;
-       dword Disc_Application;
-       dword Disc_Incompat;
-       dword Disc_No_Command;
-       dword Disc_Long_Msg;
-       dword Disc_Supervisor;
-       dword Disc_SUB_SEP_PWD;
-       dword Disc_Invalid_Msg;
-       dword Disc_Page_Coding;
-       dword Disc_App_Timeout;
-       dword Disc_Unspecified;
-} diva_fax_call_statistics_t;
-
-typedef struct _diva_prot_statistics {
-       dword X_Frames;
-       dword X_Bytes;
-       dword X_Errors;
-       dword R_Frames;
-       dword R_Bytes;
-       dword R_Errors;
-} diva_prot_statistics_t;
-
-typedef struct _diva_ifc_statistics {
-       diva_incoming_call_statistics_t inc;
-       diva_outgoing_call_statistics_t outg;
-       diva_modem_call_statistics_t mdm;
-       diva_fax_call_statistics_t fax;
-       diva_prot_statistics_t b1;
-       diva_prot_statistics_t b2;
-       diva_prot_statistics_t d1;
-       diva_prot_statistics_t d2;
-} diva_ifc_statistics_t;
-
-/*
-  Structure used to represent "State\\BX" directory
-  to user.
-*/
-typedef struct _diva_trace_line_state {
-       dword   ChannelNumber;
-
-       char Line[DIVA_TRACE_LINE_TYPE_LEN];
-
-       char Framing[DIVA_TRACE_LINE_TYPE_LEN];
-
-       char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
-       char Layer3[DIVA_TRACE_LINE_TYPE_LEN];
-
-       char RemoteAddress[DIVA_TRACE_LINE_TYPE_LEN];
-       char RemoteSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
-
-       char LocalAddress[DIVA_TRACE_LINE_TYPE_LEN];
-       char LocalSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
-
-       diva_trace_ie_t call_BC;
-       diva_trace_ie_t call_HLC;
-       diva_trace_ie_t call_LLC;
-
-       dword Charges;
-
-       dword CallReference;
-
-       dword LastDisconnecCause;
-
-       char UserID[DIVA_TRACE_LINE_TYPE_LEN];
-
-       diva_trace_modem_state_t modem;
-       diva_trace_fax_state_t fax;
-
-       diva_trace_interface_state_t *pInterface;
-
-       diva_ifc_statistics_t *pInterfaceStat;
-
-} diva_trace_line_state_t;
-
-#define DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE             ('l')
-#define DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE            ('m')
-#define DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE              ('f')
-#define DIVA_SUPER_TRACE_INTERFACE_CHANGE               ('i')
-#define DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE             ('s')
-#define DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE         ('M')
-#define DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE         ('F')
-
-struct _diva_strace_library_interface;
-typedef void (*diva_trace_channel_state_change_proc_t)(void *user_context,
-                                                      struct _diva_strace_library_interface *hLib,
-                                                      int Adapter,
-                                                      diva_trace_line_state_t *channel, int notify_subject);
-typedef void (*diva_trace_channel_trace_proc_t)(void *user_context,
-                                               struct _diva_strace_library_interface *hLib,
-                                               int Adapter, void *xlog_buffer, int length);
-typedef void (*diva_trace_error_proc_t)(void *user_context,
-                                       struct _diva_strace_library_interface *hLib,
-                                       int Adapter,
-                                       int error, const char *file, int line);
-
-/*
-  This structure creates interface from user to library
-*/
-typedef struct _diva_trace_library_user_interface {
-       void *user_context;
-       diva_trace_channel_state_change_proc_t notify_proc;
-       diva_trace_channel_trace_proc_t trace_proc;
-       diva_trace_error_proc_t error_notify_proc;
-} diva_trace_library_user_interface_t;
-
-/*
-  Interface from Library to User
-*/
-typedef int (*DivaSTraceLibraryStart_proc_t)(void *hLib);
-typedef int (*DivaSTraceLibraryFinit_proc_t)(void *hLib);
-typedef int (*DivaSTraceMessageInput_proc_t)(void *hLib);
-typedef void* (*DivaSTraceGetHandle_proc_t)(void *hLib);
-
-/*
-  Turn Audio Tap trace on/off
-  Channel should be in the range 1 ... Number of Channels
-*/
-typedef int (*DivaSTraceSetAudioTap_proc_t)(void *hLib, int Channel, int on);
-
-/*
-  Turn B-channel trace on/off
-  Channel should be in the range 1 ... Number of Channels
-*/
-typedef int (*DivaSTraceSetBChannel_proc_t)(void *hLib, int Channel, int on);
-
-/*
-  Turn D-channel (Layer1/Layer2/Layer3) trace on/off
-  Layer1 - All D-channel frames received/sent over the interface
-  inclusive Layer 2 headers, Layer 2 frames and TEI management frames
-  Layer2 - Events from LAPD protocol instance with SAPI of signalling protocol
-  Layer3 - All D-channel frames addressed to assigned to the card TEI and
-  SAPI of signalling protocol, and signalling protocol events.
-*/
-typedef int (*DivaSTraceSetDChannel_proc_t)(void *hLib, int on);
-
-/*
-  Get overall card statistics
-*/
-typedef int (*DivaSTraceGetOutgoingCallStatistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetIncomingCallStatistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetModemStatistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetFaxStatistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetBLayer1Statistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetBLayer2Statistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetDLayer1Statistics_proc_t)(void *hLib);
-typedef int (*DivaSTraceGetDLayer2Statistics_proc_t)(void *hLib);
-
-/*
-  Call control
-*/
-typedef int (*DivaSTraceClearCall_proc_t)(void *hLib, int Channel);
-
-typedef struct _diva_strace_library_interface {
-       void *hLib;
-       DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStart;
-       DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStop;
-       DivaSTraceLibraryFinit_proc_t DivaSTraceLibraryFinit;
-       DivaSTraceMessageInput_proc_t DivaSTraceMessageInput;
-       DivaSTraceGetHandle_proc_t DivaSTraceGetHandle;
-       DivaSTraceSetAudioTap_proc_t DivaSTraceSetAudioTap;
-       DivaSTraceSetBChannel_proc_t DivaSTraceSetBChannel;
-       DivaSTraceSetDChannel_proc_t DivaSTraceSetDChannel;
-       DivaSTraceSetDChannel_proc_t DivaSTraceSetInfo;
-       DivaSTraceGetOutgoingCallStatistics_proc_t \
-       DivaSTraceGetOutgoingCallStatistics;
-       DivaSTraceGetIncomingCallStatistics_proc_t \
-       DivaSTraceGetIncomingCallStatistics;
-       DivaSTraceGetModemStatistics_proc_t \
-       DivaSTraceGetModemStatistics;
-       DivaSTraceGetFaxStatistics_proc_t \
-       DivaSTraceGetFaxStatistics;
-       DivaSTraceGetBLayer1Statistics_proc_t \
-       DivaSTraceGetBLayer1Statistics;
-       DivaSTraceGetBLayer2Statistics_proc_t \
-       DivaSTraceGetBLayer2Statistics;
-       DivaSTraceGetDLayer1Statistics_proc_t \
-       DivaSTraceGetDLayer1Statistics;
-       DivaSTraceGetDLayer2Statistics_proc_t \
-       DivaSTraceGetDLayer2Statistics;
-       DivaSTraceClearCall_proc_t DivaSTraceClearCall;
-} diva_strace_library_interface_t;
-
-/*
-  Create and return Library interface
-*/
-diva_strace_library_interface_t *DivaSTraceLibraryCreateInstance(int Adapter,
-                                                                const diva_trace_library_user_interface_t *user_proc,
-                                                                byte *pmem);
-dword DivaSTraceGetMemotyRequirement(int channels);
-
-#define DIVA_MAX_ADAPTERS  64
-#define DIVA_MAX_LINES     32
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c
deleted file mode 100644 (file)
index 2ee789f..0000000
+++ /dev/null
@@ -1,2194 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2000.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    1.9
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "kst_ifc.h"
-#include "di_defs.h"
-#include "maintidi.h"
-#include "pc.h"
-#include "man_defs.h"
-
-
-extern void diva_mnt_internal_dprintf(dword drv_id, dword type, char *p, ...);
-
-#define MODEM_PARSE_ENTRIES  16 /* amount of variables of interest */
-#define FAX_PARSE_ENTRIES    12 /* amount of variables of interest */
-#define LINE_PARSE_ENTRIES   15 /* amount of variables of interest */
-#define STAT_PARSE_ENTRIES   70 /* amount of variables of interest */
-
-/*
-  LOCAL FUNCTIONS
-*/
-static int DivaSTraceLibraryStart(void *hLib);
-static int DivaSTraceLibraryStop(void *hLib);
-static int SuperTraceLibraryFinit(void *hLib);
-static void *SuperTraceGetHandle(void *hLib);
-static int SuperTraceMessageInput(void *hLib);
-static int SuperTraceSetAudioTap(void *hLib, int Channel, int on);
-static int SuperTraceSetBChannel(void *hLib, int Channel, int on);
-static int SuperTraceSetDChannel(void *hLib, int on);
-static int SuperTraceSetInfo(void *hLib, int on);
-static int SuperTraceClearCall(void *hLib, int Channel);
-static int SuperTraceGetOutgoingCallStatistics(void *hLib);
-static int SuperTraceGetIncomingCallStatistics(void *hLib);
-static int SuperTraceGetModemStatistics(void *hLib);
-static int SuperTraceGetFaxStatistics(void *hLib);
-static int SuperTraceGetBLayer1Statistics(void *hLib);
-static int SuperTraceGetBLayer2Statistics(void *hLib);
-static int SuperTraceGetDLayer1Statistics(void *hLib);
-static int SuperTraceGetDLayer2Statistics(void *hLib);
-
-/*
-  LOCAL FUNCTIONS
-*/
-static int ScheduleNextTraceRequest(diva_strace_context_t *pLib);
-static int process_idi_event(diva_strace_context_t *pLib,
-                            diva_man_var_header_t *pVar);
-static int process_idi_info(diva_strace_context_t *pLib,
-                           diva_man_var_header_t *pVar);
-static int diva_modem_event(diva_strace_context_t *pLib, int Channel);
-static int diva_fax_event(diva_strace_context_t *pLib, int Channel);
-static int diva_line_event(diva_strace_context_t *pLib, int Channel);
-static int diva_modem_info(diva_strace_context_t *pLib,
-                          int Channel,
-                          diva_man_var_header_t *pVar);
-static int diva_fax_info(diva_strace_context_t *pLib,
-                        int Channel,
-                        diva_man_var_header_t *pVar);
-static int diva_line_info(diva_strace_context_t *pLib,
-                         int Channel,
-                         diva_man_var_header_t *pVar);
-static int diva_ifc_statistics(diva_strace_context_t *pLib,
-                              diva_man_var_header_t *pVar);
-static diva_man_var_header_t *get_next_var(diva_man_var_header_t *pVar);
-static diva_man_var_header_t *find_var(diva_man_var_header_t *pVar,
-                                      const char *name);
-static int diva_strace_read_int(diva_man_var_header_t *pVar, int *var);
-static int diva_strace_read_uint(diva_man_var_header_t *pVar, dword *var);
-static int diva_strace_read_asz(diva_man_var_header_t *pVar, char *var);
-static int diva_strace_read_asc(diva_man_var_header_t *pVar, char *var);
-static int diva_strace_read_ie(diva_man_var_header_t *pVar,
-                              diva_trace_ie_t *var);
-static void diva_create_parse_table(diva_strace_context_t *pLib);
-static void diva_trace_error(diva_strace_context_t *pLib,
-                            int error, const char *file, int line);
-static void diva_trace_notify_user(diva_strace_context_t *pLib,
-                                  int Channel,
-                                  int notify_subject);
-static int diva_trace_read_variable(diva_man_var_header_t *pVar,
-                                   void *variable);
-
-/*
-  Initialize the library and return context
-  of the created trace object that will represent
-  the IDI adapter.
-  Return 0 on error.
-*/
-diva_strace_library_interface_t *DivaSTraceLibraryCreateInstance(int Adapter,
-                                                                const diva_trace_library_user_interface_t *user_proc,
-                                                                byte *pmem) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)pmem;
-       int i;
-
-       if (!pLib) {
-               return NULL;
-       }
-
-       pmem += sizeof(*pLib);
-       memset(pLib, 0x00, sizeof(*pLib));
-
-       pLib->Adapter  = Adapter;
-
-       /*
-         Set up Library Interface
-       */
-       pLib->instance.hLib                                = pLib;
-       pLib->instance.DivaSTraceLibraryStart              = DivaSTraceLibraryStart;
-       pLib->instance.DivaSTraceLibraryStop               = DivaSTraceLibraryStop;
-       pLib->instance.DivaSTraceLibraryFinit              = SuperTraceLibraryFinit;
-       pLib->instance.DivaSTraceMessageInput              = SuperTraceMessageInput;
-       pLib->instance.DivaSTraceGetHandle                 = SuperTraceGetHandle;
-       pLib->instance.DivaSTraceSetAudioTap               = SuperTraceSetAudioTap;
-       pLib->instance.DivaSTraceSetBChannel               = SuperTraceSetBChannel;
-       pLib->instance.DivaSTraceSetDChannel               = SuperTraceSetDChannel;
-       pLib->instance.DivaSTraceSetInfo                   = SuperTraceSetInfo;
-       pLib->instance.DivaSTraceGetOutgoingCallStatistics = \
-               SuperTraceGetOutgoingCallStatistics;
-       pLib->instance.DivaSTraceGetIncomingCallStatistics = \
-               SuperTraceGetIncomingCallStatistics;
-       pLib->instance.DivaSTraceGetModemStatistics        = \
-               SuperTraceGetModemStatistics;
-       pLib->instance.DivaSTraceGetFaxStatistics          = \
-               SuperTraceGetFaxStatistics;
-       pLib->instance.DivaSTraceGetBLayer1Statistics      = \
-               SuperTraceGetBLayer1Statistics;
-       pLib->instance.DivaSTraceGetBLayer2Statistics      = \
-               SuperTraceGetBLayer2Statistics;
-       pLib->instance.DivaSTraceGetDLayer1Statistics      = \
-               SuperTraceGetDLayer1Statistics;
-       pLib->instance.DivaSTraceGetDLayer2Statistics      = \
-               SuperTraceGetDLayer2Statistics;
-       pLib->instance.DivaSTraceClearCall                 = SuperTraceClearCall;
-
-
-       if (user_proc) {
-               pLib->user_proc_table.user_context      = user_proc->user_context;
-               pLib->user_proc_table.notify_proc       = user_proc->notify_proc;
-               pLib->user_proc_table.trace_proc        = user_proc->trace_proc;
-               pLib->user_proc_table.error_notify_proc = user_proc->error_notify_proc;
-       }
-
-       if (!(pLib->hAdapter = SuperTraceOpenAdapter(Adapter))) {
-               diva_mnt_internal_dprintf(0, DLI_ERR, "Can not open XDI adapter");
-               return NULL;
-       }
-       pLib->Channels = SuperTraceGetNumberOfChannels(pLib->hAdapter);
-
-       /*
-         Calculate amount of parte table entites necessary to translate
-         information from all events of onterest
-       */
-       pLib->parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
-                              STAT_PARSE_ENTRIES + \
-                              LINE_PARSE_ENTRIES + 1) * pLib->Channels;
-       pLib->parse_table = (diva_strace_path2action_t *)pmem;
-
-       for (i = 0; i < 30; i++) {
-               pLib->lines[i].pInterface     = &pLib->Interface;
-               pLib->lines[i].pInterfaceStat = &pLib->InterfaceStat;
-       }
-
-       pLib->e.R = &pLib->RData;
-
-       pLib->req_busy = 1;
-       pLib->rc_ok    = ASSIGN_OK;
-
-       diva_create_parse_table(pLib);
-
-       return ((diva_strace_library_interface_t *)pLib);
-}
-
-static int DivaSTraceLibraryStart(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       return (SuperTraceASSIGN(pLib->hAdapter, pLib->buffer));
-}
-
-/*
-  Return (-1) on error
-  Return (0) if was initiated or pending
-  Return (1) if removal is complete
-*/
-static int DivaSTraceLibraryStop(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if (!pLib->e.Id) { /* Was never started/assigned */
-               return (1);
-       }
-
-       switch (pLib->removal_state) {
-       case 0:
-               pLib->removal_state = 1;
-               ScheduleNextTraceRequest(pLib);
-               break;
-
-       case 3:
-               return (1);
-       }
-
-       return (0);
-}
-
-static int SuperTraceLibraryFinit(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       if (pLib) {
-               if (pLib->hAdapter) {
-                       SuperTraceCloseAdapter(pLib->hAdapter);
-               }
-               return (0);
-       }
-       return (-1);
-}
-
-static void *SuperTraceGetHandle(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       return (&pLib->e);
-}
-
-/*
-  After library handle object is gone in signaled state
-  this function should be called and will pick up incoming
-  IDI messages (return codes and indications).
-*/
-static int SuperTraceMessageInput(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       int ret = 0;
-       byte Rc, Ind;
-
-       if (pLib->e.complete == 255) {
-               /*
-                 Process return code
-               */
-               pLib->req_busy = 0;
-               Rc             = pLib->e.Rc;
-               pLib->e.Rc     = 0;
-
-               if (pLib->removal_state == 2) {
-                       pLib->removal_state = 3;
-                       return (0);
-               }
-
-               if (Rc != pLib->rc_ok) {
-                       int ignore = 0;
-                       /*
-                         Auto-detect amount of events/channels and features
-                       */
-                       if (pLib->general_b_ch_event == 1) {
-                               pLib->general_b_ch_event = 2;
-                               ignore = 1;
-                       } else if (pLib->general_fax_event == 1) {
-                               pLib->general_fax_event = 2;
-                               ignore = 1;
-                       } else if (pLib->general_mdm_event == 1) {
-                               pLib->general_mdm_event = 2;
-                               ignore = 1;
-                       } else if ((pLib->ChannelsTraceActive < pLib->Channels) && pLib->ChannelsTraceActive) {
-                               pLib->ChannelsTraceActive = pLib->Channels;
-                               ignore = 1;
-                       } else if (pLib->ModemTraceActive < pLib->Channels) {
-                               pLib->ModemTraceActive = pLib->Channels;
-                               ignore = 1;
-                       } else if (pLib->FaxTraceActive < pLib->Channels) {
-                               pLib->FaxTraceActive = pLib->Channels;
-                               ignore = 1;
-                       } else if (pLib->audio_trace_init == 2) {
-                               ignore = 1;
-                               pLib->audio_trace_init = 1;
-                       } else if (pLib->eye_pattern_pending) {
-                               pLib->eye_pattern_pending =  0;
-                               ignore = 1;
-                       } else if (pLib->audio_tap_pending) {
-                               pLib->audio_tap_pending = 0;
-                               ignore = 1;
-                       }
-
-                       if (!ignore) {
-                               return (-1); /* request failed */
-                       }
-               } else {
-                       if (pLib->general_b_ch_event == 1) {
-                               pLib->ChannelsTraceActive = pLib->Channels;
-                               pLib->general_b_ch_event = 2;
-                       } else if (pLib->general_fax_event == 1) {
-                               pLib->general_fax_event = 2;
-                               pLib->FaxTraceActive = pLib->Channels;
-                       } else if (pLib->general_mdm_event == 1) {
-                               pLib->general_mdm_event = 2;
-                               pLib->ModemTraceActive = pLib->Channels;
-                       }
-               }
-               if (pLib->audio_trace_init == 2) {
-                       pLib->audio_trace_init = 1;
-               }
-               pLib->rc_ok = 0xff; /* default OK after assign was done */
-               if ((ret = ScheduleNextTraceRequest(pLib))) {
-                       return (-1);
-               }
-       } else {
-               /*
-                 Process indication
-                 Always 'RNR' indication if return code is pending
-               */
-               Ind         = pLib->e.Ind;
-               pLib->e.Ind = 0;
-               if (pLib->removal_state) {
-                       pLib->e.RNum    = 0;
-                       pLib->e.RNR     = 2;
-               } else if (pLib->req_busy) {
-                       pLib->e.RNum    = 0;
-                       pLib->e.RNR     = 1;
-               } else {
-                       if (pLib->e.complete != 0x02) {
-                               /*
-                                 Look-ahead call, set up buffers
-                               */
-                               pLib->e.RNum       = 1;
-                               pLib->e.R->P       = (byte *)&pLib->buffer[0];
-                               pLib->e.R->PLength = (word)(sizeof(pLib->buffer) - 1);
-
-                       } else {
-                               /*
-                                 Indication reception complete, process it now
-                               */
-                               byte *p = (byte *)&pLib->buffer[0];
-                               pLib->buffer[pLib->e.R->PLength] = 0; /* terminate I.E. with zero */
-
-                               switch (Ind) {
-                               case MAN_COMBI_IND: {
-                                       int total_length    = pLib->e.R->PLength;
-                                       word  this_ind_length;
-
-                                       while (total_length > 3 && *p) {
-                                               Ind = *p++;
-                                               this_ind_length = (word)p[0] | ((word)p[1] << 8);
-                                               p += 2;
-
-                                               switch (Ind) {
-                                               case MAN_INFO_IND:
-                                                       if (process_idi_info(pLib, (diva_man_var_header_t *)p)) {
-                                                               return (-1);
-                                                       }
-                                                       break;
-                                               case MAN_EVENT_IND:
-                                                       if (process_idi_event(pLib, (diva_man_var_header_t *)p)) {
-                                                               return (-1);
-                                                       }
-                                                       break;
-                                               case MAN_TRACE_IND:
-                                                       if (pLib->trace_on == 1) {
-                                                               /*
-                                                                 Ignore first trace event that is result of
-                                                                 EVENT_ON operation
-                                                               */
-                                                               pLib->trace_on++;
-                                                       } else {
-                                                               /*
-                                                                 Delivery XLOG buffer to application
-                                                               */
-                                                               if (pLib->user_proc_table.trace_proc) {
-                                                                       (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
-                                                                                                             &pLib->instance, pLib->Adapter,
-                                                                                                             p, this_ind_length);
-                                                               }
-                                                       }
-                                                       break;
-                                               default:
-                                                       diva_mnt_internal_dprintf(0, DLI_ERR, "Unknown IDI Ind (DMA mode): %02x", Ind);
-                                               }
-                                               p += (this_ind_length + 1);
-                                               total_length -= (4 + this_ind_length);
-                                       }
-                               } break;
-                               case MAN_INFO_IND:
-                                       if (process_idi_info(pLib, (diva_man_var_header_t *)p)) {
-                                               return (-1);
-                                       }
-                                       break;
-                               case MAN_EVENT_IND:
-                                       if (process_idi_event(pLib, (diva_man_var_header_t *)p)) {
-                                               return (-1);
-                                       }
-                                       break;
-                               case MAN_TRACE_IND:
-                                       if (pLib->trace_on == 1) {
-                                               /*
-                                                 Ignore first trace event that is result of
-                                                 EVENT_ON operation
-                                               */
-                                               pLib->trace_on++;
-                                       } else {
-                                               /*
-                                                 Delivery XLOG buffer to application
-                                               */
-                                               if (pLib->user_proc_table.trace_proc) {
-                                                       (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
-                                                                                             &pLib->instance, pLib->Adapter,
-                                                                                             p, pLib->e.R->PLength);
-                                               }
-                                       }
-                                       break;
-                               default:
-                                       diva_mnt_internal_dprintf(0, DLI_ERR, "Unknown IDI Ind: %02x", Ind);
-                               }
-                       }
-               }
-       }
-
-       if ((ret = ScheduleNextTraceRequest(pLib))) {
-               return (-1);
-       }
-
-       return (ret);
-}
-
-/*
-  Internal state machine responsible for scheduling of requests
-*/
-static int ScheduleNextTraceRequest(diva_strace_context_t *pLib) {
-       char name[64];
-       int ret = 0;
-       int i;
-
-       if (pLib->req_busy) {
-               return (0);
-       }
-
-       if (pLib->removal_state == 1) {
-               if (SuperTraceREMOVE(pLib->hAdapter)) {
-                       pLib->removal_state = 3;
-               } else {
-                       pLib->req_busy = 1;
-                       pLib->removal_state = 2;
-               }
-               return (0);
-       }
-
-       if (pLib->removal_state) {
-               return (0);
-       }
-
-       if (!pLib->general_b_ch_event) {
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\B Event", pLib->buffer))) {
-                       return (-1);
-               }
-               pLib->general_b_ch_event = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->general_fax_event) {
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\FAX Event", pLib->buffer))) {
-                       return (-1);
-               }
-               pLib->general_fax_event = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->general_mdm_event) {
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\Modem Event", pLib->buffer))) {
-                       return (-1);
-               }
-               pLib->general_mdm_event = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->ChannelsTraceActive < pLib->Channels) {
-               pLib->ChannelsTraceActive++;
-               sprintf(name, "State\\B%d\\Line", pLib->ChannelsTraceActive);
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->ChannelsTraceActive--;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->ModemTraceActive < pLib->Channels) {
-               pLib->ModemTraceActive++;
-               sprintf(name, "State\\B%d\\Modem\\Event", pLib->ModemTraceActive);
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->ModemTraceActive--;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->FaxTraceActive < pLib->Channels) {
-               pLib->FaxTraceActive++;
-               sprintf(name, "State\\B%d\\FAX\\Event", pLib->FaxTraceActive);
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->FaxTraceActive--;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->trace_mask_init) {
-               word tmp = 0x0000;
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\Event Enable",
-                                      &tmp,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(tmp))) {
-                       return (-1);
-               }
-               pLib->trace_mask_init = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->audio_trace_init) {
-               dword tmp = 0x00000000;
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\AudioCh# Enable",
-                                      &tmp,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(tmp))) {
-                       return (-1);
-               }
-               pLib->audio_trace_init = 2;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->bchannel_init) {
-               dword tmp = 0x00000000;
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\B-Ch# Enable",
-                                      &tmp,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(tmp))) {
-                       return (-1);
-               }
-               pLib->bchannel_init = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->trace_length_init) {
-               word tmp = 30;
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\Max Log Length",
-                                      &tmp,
-                                      0x82, /* MI_UINT */
-                                       sizeof(tmp))) {
-                       return (-1);
-               }
-               pLib->trace_length_init = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->trace_on) {
-               if (SuperTraceTraceOnRequest(pLib->hAdapter,
-                                            "Trace\\Log Buffer",
-                                            pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->trace_on = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->trace_event_mask != pLib->current_trace_event_mask) {
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\Event Enable",
-                                      &pLib->trace_event_mask,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(pLib->trace_event_mask))) {
-                       return (-1);
-               }
-               pLib->current_trace_event_mask = pLib->trace_event_mask;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if ((pLib->audio_tap_pending >= 0) && (pLib->audio_tap_mask != pLib->current_audio_tap_mask)) {
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\AudioCh# Enable",
-                                      &pLib->audio_tap_mask,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(pLib->audio_tap_mask))) {
-                       return (-1);
-               }
-               pLib->current_audio_tap_mask = pLib->audio_tap_mask;
-               pLib->audio_tap_pending = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if ((pLib->eye_pattern_pending >= 0) && (pLib->audio_tap_mask != pLib->current_eye_pattern_mask)) {
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\EyeCh# Enable",
-                                      &pLib->audio_tap_mask,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(pLib->audio_tap_mask))) {
-                       return (-1);
-               }
-               pLib->current_eye_pattern_mask = pLib->audio_tap_mask;
-               pLib->eye_pattern_pending = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->bchannel_trace_mask != pLib->current_bchannel_trace_mask) {
-               if (SuperTraceWriteVar(pLib->hAdapter,
-                                      pLib->buffer,
-                                      "Trace\\B-Ch# Enable",
-                                      &pLib->bchannel_trace_mask,
-                                      0x87, /* MI_BITFLD */
-                                       sizeof(pLib->bchannel_trace_mask))) {
-                       return (-1);
-               }
-               pLib->current_bchannel_trace_mask = pLib->bchannel_trace_mask;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->trace_events_down) {
-               if (SuperTraceTraceOnRequest(pLib->hAdapter,
-                                            "Events Down",
-                                            pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->trace_events_down = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->l1_trace) {
-               if (SuperTraceTraceOnRequest(pLib->hAdapter,
-                                            "State\\Layer1",
-                                            pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->l1_trace = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->l2_trace) {
-               if (SuperTraceTraceOnRequest(pLib->hAdapter,
-                                            "State\\Layer2 No1",
-                                            pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->l2_trace = 1;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       for (i = 0; i < 30; i++) {
-               if (pLib->pending_line_status & (1L << i)) {
-                       sprintf(name, "State\\B%d", i + 1);
-                       if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) {
-                               return (-1);
-                       }
-                       pLib->pending_line_status &= ~(1L << i);
-                       pLib->req_busy = 1;
-                       return (0);
-               }
-               if (pLib->pending_modem_status & (1L << i)) {
-                       sprintf(name, "State\\B%d\\Modem", i + 1);
-                       if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) {
-                               return (-1);
-                       }
-                       pLib->pending_modem_status &= ~(1L << i);
-                       pLib->req_busy = 1;
-                       return (0);
-               }
-               if (pLib->pending_fax_status & (1L << i)) {
-                       sprintf(name, "State\\B%d\\FAX", i + 1);
-                       if (SuperTraceReadRequest(pLib->hAdapter, name, pLib->buffer)) {
-                               return (-1);
-                       }
-                       pLib->pending_fax_status &= ~(1L << i);
-                       pLib->req_busy = 1;
-                       return (0);
-               }
-               if (pLib->clear_call_command & (1L << i)) {
-                       sprintf(name, "State\\B%d\\Clear Call", i + 1);
-                       if (SuperTraceExecuteRequest(pLib->hAdapter, name, pLib->buffer)) {
-                               return (-1);
-                       }
-                       pLib->clear_call_command &= ~(1L << i);
-                       pLib->req_busy = 1;
-                       return (0);
-               }
-       }
-
-       if (pLib->outgoing_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\Outgoing Calls",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->outgoing_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->incoming_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\Incoming Calls",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->incoming_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->modem_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\Modem",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->modem_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->fax_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\FAX",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->fax_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->b1_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\B-Layer1",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->b1_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->b2_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\B-Layer2",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->b2_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->d1_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\D-Layer1",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->d1_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (pLib->d2_ifc_stats) {
-               if (SuperTraceReadRequest(pLib->hAdapter,
-                                         "Statistics\\D-Layer2",
-                                         pLib->buffer)) {
-                       return (-1);
-               }
-               pLib->d2_ifc_stats = 0;
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       if (!pLib->IncomingCallsCallsActive) {
-               pLib->IncomingCallsCallsActive = 1;
-               sprintf(name, "%s", "Statistics\\Incoming Calls\\Calls");
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->IncomingCallsCallsActive = 0;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-       if (!pLib->IncomingCallsConnectedActive) {
-               pLib->IncomingCallsConnectedActive = 1;
-               sprintf(name, "%s", "Statistics\\Incoming Calls\\Connected");
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->IncomingCallsConnectedActive = 0;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-       if (!pLib->OutgoingCallsCallsActive) {
-               pLib->OutgoingCallsCallsActive = 1;
-               sprintf(name, "%s", "Statistics\\Outgoing Calls\\Calls");
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->OutgoingCallsCallsActive = 0;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-       if (!pLib->OutgoingCallsConnectedActive) {
-               pLib->OutgoingCallsConnectedActive = 1;
-               sprintf(name, "%s", "Statistics\\Outgoing Calls\\Connected");
-               if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
-                       pLib->OutgoingCallsConnectedActive = 0;
-                       return (-1);
-               }
-               pLib->req_busy = 1;
-               return (0);
-       }
-
-       return (0);
-}
-
-static int process_idi_event(diva_strace_context_t *pLib,
-                            diva_man_var_header_t *pVar) {
-       const char *path = (char *)&pVar->path_length + 1;
-       char name[64];
-       int i;
-
-       if (!strncmp("State\\B Event", path, pVar->path_length)) {
-               dword ch_id;
-               if (!diva_trace_read_variable(pVar, &ch_id)) {
-                       if (!pLib->line_init_event && !pLib->pending_line_status) {
-                               for (i = 1; i <= pLib->Channels; i++) {
-                                       diva_line_event(pLib, i);
-                               }
-                               return (0);
-                       } else if (ch_id && ch_id <= pLib->Channels) {
-                               return (diva_line_event(pLib, (int)ch_id));
-                       }
-                       return (0);
-               }
-               return (-1);
-       }
-
-       if (!strncmp("State\\FAX Event", path, pVar->path_length)) {
-               dword ch_id;
-               if (!diva_trace_read_variable(pVar, &ch_id)) {
-                       if (!pLib->pending_fax_status && !pLib->fax_init_event) {
-                               for (i = 1; i <= pLib->Channels; i++) {
-                                       diva_fax_event(pLib, i);
-                               }
-                               return (0);
-                       } else if (ch_id && ch_id <= pLib->Channels) {
-                               return (diva_fax_event(pLib, (int)ch_id));
-                       }
-                       return (0);
-               }
-               return (-1);
-       }
-
-       if (!strncmp("State\\Modem Event", path, pVar->path_length)) {
-               dword ch_id;
-               if (!diva_trace_read_variable(pVar, &ch_id)) {
-                       if (!pLib->pending_modem_status && !pLib->modem_init_event) {
-                               for (i = 1; i <= pLib->Channels; i++) {
-                                       diva_modem_event(pLib, i);
-                               }
-                               return (0);
-                       } else if (ch_id && ch_id <= pLib->Channels) {
-                               return (diva_modem_event(pLib, (int)ch_id));
-                       }
-                       return (0);
-               }
-               return (-1);
-       }
-
-       /*
-         First look for Line Event
-       */
-       for (i = 1; i <= pLib->Channels; i++) {
-               sprintf(name, "State\\B%d\\Line", i);
-               if (find_var(pVar, name)) {
-                       return (diva_line_event(pLib, i));
-               }
-       }
-
-       /*
-         Look for Moden Progress Event
-       */
-       for (i = 1; i <= pLib->Channels; i++) {
-               sprintf(name, "State\\B%d\\Modem\\Event", i);
-               if (find_var(pVar, name)) {
-                       return (diva_modem_event(pLib, i));
-               }
-       }
-
-       /*
-         Look for Fax Event
-       */
-       for (i = 1; i <= pLib->Channels; i++) {
-               sprintf(name, "State\\B%d\\FAX\\Event", i);
-               if (find_var(pVar, name)) {
-                       return (diva_fax_event(pLib, i));
-               }
-       }
-
-       /*
-         Notification about loss of events
-       */
-       if (!strncmp("Events Down", path, pVar->path_length)) {
-               if (pLib->trace_events_down == 1) {
-                       pLib->trace_events_down = 2;
-               } else {
-                       diva_trace_error(pLib, 1, "Events Down", 0);
-               }
-               return (0);
-       }
-
-       if (!strncmp("State\\Layer1", path, pVar->path_length)) {
-               diva_strace_read_asz(pVar, &pLib->lines[0].pInterface->Layer1[0]);
-               if (pLib->l1_trace == 1) {
-                       pLib->l1_trace = 2;
-               } else {
-                       diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
-               }
-               return (0);
-       }
-       if (!strncmp("State\\Layer2 No1", path, pVar->path_length)) {
-               char *tmp = &pLib->lines[0].pInterface->Layer2[0];
-               dword l2_state;
-               if (diva_strace_read_uint(pVar, &l2_state))
-                       return -1;
-
-               switch (l2_state) {
-               case 0:
-                       strcpy(tmp, "Idle");
-                       break;
-               case 1:
-                       strcpy(tmp, "Layer2 UP");
-                       break;
-               case 2:
-                       strcpy(tmp, "Layer2 Disconnecting");
-                       break;
-               case 3:
-                       strcpy(tmp, "Layer2 Connecting");
-                       break;
-               case 4:
-                       strcpy(tmp, "SPID Initializing");
-                       break;
-               case 5:
-                       strcpy(tmp, "SPID Initialised");
-                       break;
-               case 6:
-                       strcpy(tmp, "Layer2 Connecting");
-                       break;
-
-               case  7:
-                       strcpy(tmp, "Auto SPID Stopped");
-                       break;
-
-               case  8:
-                       strcpy(tmp, "Auto SPID Idle");
-                       break;
-
-               case  9:
-                       strcpy(tmp, "Auto SPID Requested");
-                       break;
-
-               case  10:
-                       strcpy(tmp, "Auto SPID Delivery");
-                       break;
-
-               case 11:
-                       strcpy(tmp, "Auto SPID Complete");
-                       break;
-
-               default:
-                       sprintf(tmp, "U:%d", (int)l2_state);
-               }
-               if (pLib->l2_trace == 1) {
-                       pLib->l2_trace = 2;
-               } else {
-                       diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
-               }
-               return (0);
-       }
-
-       if (!strncmp("Statistics\\Incoming Calls\\Calls", path, pVar->path_length) ||
-           !strncmp("Statistics\\Incoming Calls\\Connected", path, pVar->path_length)) {
-               return (SuperTraceGetIncomingCallStatistics(pLib));
-       }
-
-       if (!strncmp("Statistics\\Outgoing Calls\\Calls", path, pVar->path_length) ||
-           !strncmp("Statistics\\Outgoing Calls\\Connected", path, pVar->path_length)) {
-               return (SuperTraceGetOutgoingCallStatistics(pLib));
-       }
-
-       return (-1);
-}
-
-static int diva_line_event(diva_strace_context_t *pLib, int Channel) {
-       pLib->pending_line_status |= (1L << (Channel - 1));
-       return (0);
-}
-
-static int diva_modem_event(diva_strace_context_t *pLib, int Channel) {
-       pLib->pending_modem_status |= (1L << (Channel - 1));
-       return (0);
-}
-
-static int diva_fax_event(diva_strace_context_t *pLib, int Channel) {
-       pLib->pending_fax_status |= (1L << (Channel - 1));
-       return (0);
-}
-
-/*
-  Process INFO indications that arrive from the card
-  Uses path of first I.E. to detect the source of the
-  infication
-*/
-static int process_idi_info(diva_strace_context_t *pLib,
-                           diva_man_var_header_t *pVar) {
-       const char *path = (char *)&pVar->path_length + 1;
-       char name[64];
-       int i, len;
-
-       /*
-         First look for Modem Status Info
-       */
-       for (i = pLib->Channels; i > 0; i--) {
-               len = sprintf(name, "State\\B%d\\Modem", i);
-               if (!strncmp(name, path, len)) {
-                       return (diva_modem_info(pLib, i, pVar));
-               }
-       }
-
-       /*
-         Look for Fax Status Info
-       */
-       for (i = pLib->Channels; i > 0; i--) {
-               len = sprintf(name, "State\\B%d\\FAX", i);
-               if (!strncmp(name, path, len)) {
-                       return (diva_fax_info(pLib, i, pVar));
-               }
-       }
-
-       /*
-         Look for Line Status Info
-       */
-       for (i = pLib->Channels; i > 0; i--) {
-               len = sprintf(name, "State\\B%d", i);
-               if (!strncmp(name, path, len)) {
-                       return (diva_line_info(pLib, i, pVar));
-               }
-       }
-
-       if (!diva_ifc_statistics(pLib, pVar)) {
-               return (0);
-       }
-
-       return (-1);
-}
-
-/*
-  MODEM INSTANCE STATE UPDATE
-
-  Update Modem Status Information and issue notification to user,
-  that will inform about change in the state of modem instance, that is
-  associuated with this channel
-*/
-static int diva_modem_info(diva_strace_context_t *pLib,
-                          int Channel,
-                          diva_man_var_header_t *pVar) {
-       diva_man_var_header_t *cur;
-       int i, nr = Channel - 1;
-
-       for (i  = pLib->modem_parse_entry_first[nr];
-            i <= pLib->modem_parse_entry_last[nr]; i++) {
-               if ((cur = find_var(pVar, pLib->parse_table[i].path))) {
-                       if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) {
-                               diva_trace_error(pLib, -3, __FILE__, __LINE__);
-                               return (-1);
-                       }
-               } else {
-                       diva_trace_error(pLib, -2, __FILE__, __LINE__);
-                       return (-1);
-               }
-       }
-
-       /*
-         We do not use first event to notify user - this is the event that is
-         generated as result of EVENT ON operation and is used only to initialize
-         internal variables of application
-       */
-       if (pLib->modem_init_event & (1L << nr)) {
-               diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE);
-       } else {
-               pLib->modem_init_event |= (1L << nr);
-       }
-
-       return (0);
-}
-
-static int diva_fax_info(diva_strace_context_t *pLib,
-                        int Channel,
-                        diva_man_var_header_t *pVar) {
-       diva_man_var_header_t *cur;
-       int i, nr = Channel - 1;
-
-       for (i  = pLib->fax_parse_entry_first[nr];
-            i <= pLib->fax_parse_entry_last[nr]; i++) {
-               if ((cur = find_var(pVar, pLib->parse_table[i].path))) {
-                       if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) {
-                               diva_trace_error(pLib, -3, __FILE__, __LINE__);
-                               return (-1);
-                       }
-               } else {
-                       diva_trace_error(pLib, -2, __FILE__, __LINE__);
-                       return (-1);
-               }
-       }
-
-       /*
-         We do not use first event to notify user - this is the event that is
-         generated as result of EVENT ON operation and is used only to initialize
-         internal variables of application
-       */
-       if (pLib->fax_init_event & (1L << nr)) {
-               diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE);
-       } else {
-               pLib->fax_init_event |= (1L << nr);
-       }
-
-       return (0);
-}
-
-/*
-  LINE STATE UPDATE
-  Update Line Status Information and issue notification to user,
-  that will inform about change in the line state.
-*/
-static int diva_line_info(diva_strace_context_t *pLib,
-                         int Channel,
-                         diva_man_var_header_t *pVar) {
-       diva_man_var_header_t *cur;
-       int i, nr = Channel - 1;
-
-       for (i = pLib->line_parse_entry_first[nr];
-            i <= pLib->line_parse_entry_last[nr]; i++) {
-               if ((cur = find_var(pVar, pLib->parse_table[i].path))) {
-                       if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) {
-                               diva_trace_error(pLib, -3, __FILE__, __LINE__);
-                               return (-1);
-                       }
-               } else {
-                       diva_trace_error(pLib, -2 , __FILE__, __LINE__);
-                       return (-1);
-               }
-       }
-
-       /*
-         We do not use first event to notify user - this is the event that is
-         generated as result of EVENT ON operation and is used only to initialize
-         internal variables of application
-
-         Exception is is if the line is "online". In this case we have to notify
-         user about this confition.
-       */
-       if (pLib->line_init_event & (1L << nr)) {
-               diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
-       } else {
-               pLib->line_init_event |= (1L << nr);
-               if (strcmp(&pLib->lines[nr].Line[0], "Idle")) {
-                       diva_trace_notify_user(pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
-               }
-       }
-
-       return (0);
-}
-
-/*
-  Move position to next vatianle in the chain
-*/
-static diva_man_var_header_t *get_next_var(diva_man_var_header_t *pVar) {
-       byte *msg = (byte *)pVar;
-       byte *start;
-       int msg_length;
-
-       if (*msg != ESC) return NULL;
-
-       start = msg + 2;
-       msg_length = *(msg + 1);
-       msg = (start + msg_length);
-
-       if (*msg != ESC) return NULL;
-
-       return ((diva_man_var_header_t *)msg);
-}
-
-/*
-  Move position to variable with given name
-*/
-static diva_man_var_header_t *find_var(diva_man_var_header_t *pVar,
-                                      const char *name) {
-       const char *path;
-
-       do {
-               path = (char *)&pVar->path_length + 1;
-
-               if (!strncmp(name, path, pVar->path_length)) {
-                       break;
-               }
-       } while ((pVar = get_next_var(pVar)));
-
-       return (pVar);
-}
-
-static void diva_create_line_parse_table(diva_strace_context_t *pLib,
-                                        int Channel) {
-       diva_trace_line_state_t *pLine = &pLib->lines[Channel];
-       int nr = Channel + 1;
-
-       if ((pLib->cur_parse_entry + LINE_PARSE_ENTRIES) >= pLib->parse_entries) {
-               diva_trace_error(pLib, -1, __FILE__, __LINE__);
-               return;
-       }
-
-       pLine->ChannelNumber = nr;
-
-       pLib->line_parse_entry_first[Channel] = pLib->cur_parse_entry;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Framing", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Framing[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Line", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Line[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Layer2", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer2[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Layer3", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer3[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Remote Address", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLine->RemoteAddress[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Remote SubAddr", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLine->RemoteSubAddress[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Local Address", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLine->LocalAddress[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Local SubAddr", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLine->LocalSubAddress[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\BC", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_BC;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\HLC", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_HLC;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\LLC", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_LLC;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Charges", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Charges;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Call Reference", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->CallReference;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Last Disc Cause", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLine->LastDisconnecCause;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\User ID", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->UserID[0];
-
-       pLib->line_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
-}
-
-static void diva_create_fax_parse_table(diva_strace_context_t *pLib,
-                                       int Channel) {
-       diva_trace_fax_state_t *pFax = &pLib->lines[Channel].fax;
-       int nr = Channel + 1;
-
-       if ((pLib->cur_parse_entry + FAX_PARSE_ENTRIES) >= pLib->parse_entries) {
-               diva_trace_error(pLib, -1, __FILE__, __LINE__);
-               return;
-       }
-       pFax->ChannelNumber = nr;
-
-       pLib->fax_parse_entry_first[Channel] = pLib->cur_parse_entry;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Event", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Event;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Page Counter", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Page_Counter;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Features", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Features;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Station ID", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Station_ID[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Subaddress", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Subaddress[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Password", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Password[0];
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Speed", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Speed;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Resolution", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Resolution;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Paper Width", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Width;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Paper Length", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Length;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Scanline Time", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Scanline_Time;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\FAX\\Disc Reason", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Disc_Reason;
-
-       pLib->fax_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
-}
-
-static void diva_create_modem_parse_table(diva_strace_context_t *pLib,
-                                         int Channel) {
-       diva_trace_modem_state_t *pModem = &pLib->lines[Channel].modem;
-       int nr = Channel + 1;
-
-       if ((pLib->cur_parse_entry + MODEM_PARSE_ENTRIES) >= pLib->parse_entries) {
-               diva_trace_error(pLib, -1, __FILE__, __LINE__);
-               return;
-       }
-       pModem->ChannelNumber = nr;
-
-       pLib->modem_parse_entry_first[Channel] = pLib->cur_parse_entry;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Event", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Event;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Norm", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Norm;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Options", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Options;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\TX Speed", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->TxSpeed;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\RX Speed", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxSpeed;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Roundtrip ms", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RoundtripMsec;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Symbol Rate", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SymbolRate;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\RX Level dBm", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxLeveldBm;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Echo Level dBm", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->EchoLeveldBm;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\SNR dB", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SNRdb;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\MAE", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->MAE;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Local Retrains", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalRetrains;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Remote Retrains", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteRetrains;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Local Resyncs", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalResyncs;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Remote Resyncs", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteResyncs;
-
-       sprintf(pLib->parse_table[pLib->cur_parse_entry].path,
-               "State\\B%d\\Modem\\Disc Reason", nr);
-       pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->DiscReason;
-
-       pLib->modem_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
-}
-
-static void diva_create_parse_table(diva_strace_context_t *pLib) {
-       int i;
-
-       for (i = 0; i < pLib->Channels; i++) {
-               diva_create_line_parse_table(pLib, i);
-               diva_create_modem_parse_table(pLib, i);
-               diva_create_fax_parse_table(pLib, i);
-       }
-
-       pLib->statistic_parse_first = pLib->cur_parse_entry;
-
-       /*
-         Outgoing Calls
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\Calls");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.Calls;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\Connected");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.Connected;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\User Busy");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.User_Busy;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\No Answer");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.No_Answer;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\Wrong Number");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.Wrong_Number;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\Call Rejected");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.Call_Rejected;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Outgoing Calls\\Other Failures");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.outg.Other_Failures;
-
-       /*
-         Incoming Calls
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Calls");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Calls;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Connected");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Connected;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\User Busy");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.User_Busy;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Call Rejected");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Call_Rejected;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Wrong Number");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Wrong_Number;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Incompatible Dst");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Incompatible_Dst;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Out of Order");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Out_of_Order;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Incoming Calls\\Ignored");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.inc.Ignored;
-
-       /*
-         Modem Statistics
-       */
-       pLib->mdm_statistic_parse_first = pLib->cur_parse_entry;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Normal");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Normal;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Unspecified");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Unspecified;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Busy Tone");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Busy_Tone;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Congestion");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Congestion;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Carr. Wait");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Carr_Wait;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Trn Timeout");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Trn_Timeout;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Incompat.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Incompat;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc Frame Rej.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_Frame_Rej;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\Modem\\Disc V42bis");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.mdm.Disc_V42bis;
-
-       pLib->mdm_statistic_parse_last  = pLib->cur_parse_entry - 1;
-
-       /*
-         Fax Statistics
-       */
-       pLib->fax_statistic_parse_first = pLib->cur_parse_entry;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Normal");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Normal;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Not Ident.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Not_Ident;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc No Response");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_No_Response;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Retries");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Retries;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Unexp. Msg.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Unexp_Msg;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc No Polling.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_No_Polling;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Training");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Training;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Unexpected");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Unexpected;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Application");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Application;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Incompat.");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Incompat;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc No Command");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_No_Command;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Long Msg");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Long_Msg;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Supervisor");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Supervisor;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc SUB SEP PWD");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_SUB_SEP_PWD;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Invalid Msg");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Invalid_Msg;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Page Coding");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Page_Coding;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc App Timeout");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_App_Timeout;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\FAX\\Disc Unspecified");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.fax.Disc_Unspecified;
-
-       pLib->fax_statistic_parse_last  = pLib->cur_parse_entry - 1;
-
-       /*
-         B-Layer1"
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\X-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.X_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\X-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.X_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\X-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.X_Errors;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\R-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.R_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\R-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.R_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer1\\R-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b1.R_Errors;
-
-       /*
-         B-Layer2
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\X-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.X_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\X-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.X_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\X-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.X_Errors;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\R-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.R_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\R-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.R_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\B-Layer2\\R-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.b2.R_Errors;
-
-       /*
-         D-Layer1
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\X-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.X_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\X-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.X_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\X-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.X_Errors;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\R-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.R_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\R-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.R_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer1\\R-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d1.R_Errors;
-
-       /*
-         D-Layer2
-       */
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\X-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.X_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\X-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.X_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\X-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.X_Errors;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\R-Frames");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.R_Frames;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\R-Bytes");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.R_Bytes;
-
-       strcpy(pLib->parse_table[pLib->cur_parse_entry].path,
-              "Statistics\\D-Layer2\\R-Errors");
-       pLib->parse_table[pLib->cur_parse_entry++].variable = \
-               &pLib->InterfaceStat.d2.R_Errors;
-
-
-       pLib->statistic_parse_last  = pLib->cur_parse_entry - 1;
-}
-
-static void diva_trace_error(diva_strace_context_t *pLib,
-                            int error, const char *file, int line) {
-       if (pLib->user_proc_table.error_notify_proc) {
-               (*(pLib->user_proc_table.error_notify_proc))(\
-                       pLib->user_proc_table.user_context,
-                       &pLib->instance, pLib->Adapter,
-                       error, file, line);
-       }
-}
-
-/*
-  Delivery notification to user
-*/
-static void diva_trace_notify_user(diva_strace_context_t *pLib,
-                                  int Channel,
-                                  int notify_subject) {
-       if (pLib->user_proc_table.notify_proc) {
-               (*(pLib->user_proc_table.notify_proc))(pLib->user_proc_table.user_context,
-                                                      &pLib->instance,
-                                                      pLib->Adapter,
-                                                      &pLib->lines[Channel],
-                                                      notify_subject);
-       }
-}
-
-/*
-  Read variable value to they destination based on the variable type
-*/
-static int diva_trace_read_variable(diva_man_var_header_t *pVar,
-                                   void *variable) {
-       switch (pVar->type) {
-       case 0x03: /* MI_ASCIIZ - syting                               */
-               return (diva_strace_read_asz(pVar, (char *)variable));
-       case 0x04: /* MI_ASCII  - string                               */
-               return (diva_strace_read_asc(pVar, (char *)variable));
-       case 0x05: /* MI_NUMBER - counted sequence of bytes            */
-               return (diva_strace_read_ie(pVar, (diva_trace_ie_t *)variable));
-       case 0x81: /* MI_INT    - signed integer                       */
-               return (diva_strace_read_int(pVar, (int *)variable));
-       case 0x82: /* MI_UINT   - unsigned integer                     */
-               return (diva_strace_read_uint(pVar, (dword *)variable));
-       case 0x83: /* MI_HINT   - unsigned integer, hex representetion */
-               return (diva_strace_read_uint(pVar, (dword *)variable));
-       case 0x87: /* MI_BITFLD - unsigned integer, bit representation */
-               return (diva_strace_read_uint(pVar, (dword *)variable));
-       }
-
-       /*
-         This type of variable is not handled, indicate error
-         Or one problem in management interface, or in application recodeing
-         table, or this application should handle it.
-       */
-       return (-1);
-}
-
-/*
-  Read signed integer to destination
-*/
-static int diva_strace_read_int(diva_man_var_header_t *pVar, int *var) {
-       byte *ptr = (char *)&pVar->path_length;
-       int value;
-
-       ptr += (pVar->path_length + 1);
-
-       switch (pVar->value_length) {
-       case 1:
-               value = *(char *)ptr;
-               break;
-
-       case 2:
-               value = (short)GET_WORD(ptr);
-               break;
-
-       case 4:
-               value = (int)GET_DWORD(ptr);
-               break;
-
-       default:
-               return (-1);
-       }
-
-       *var = value;
-
-       return (0);
-}
-
-static int diva_strace_read_uint(diva_man_var_header_t *pVar, dword *var) {
-       byte *ptr = (char *)&pVar->path_length;
-       dword value;
-
-       ptr += (pVar->path_length + 1);
-
-       switch (pVar->value_length) {
-       case 1:
-               value = (byte)(*ptr);
-               break;
-
-       case 2:
-               value = (word)GET_WORD(ptr);
-               break;
-
-       case 3:
-               value  = (dword)GET_DWORD(ptr);
-               value &= 0x00ffffff;
-               break;
-
-       case 4:
-               value = (dword)GET_DWORD(ptr);
-               break;
-
-       default:
-               return (-1);
-       }
-
-       *var = value;
-
-       return (0);
-}
-
-/*
-  Read zero terminated ASCII string
-*/
-static int diva_strace_read_asz(diva_man_var_header_t *pVar, char *var) {
-       char *ptr = (char *)&pVar->path_length;
-       int length;
-
-       ptr += (pVar->path_length + 1);
-
-       if (!(length = pVar->value_length)) {
-               length = strlen(ptr);
-       }
-       memcpy(var, ptr, length);
-       var[length] = 0;
-
-       return (0);
-}
-
-/*
-  Read counted (with leading length byte) ASCII string
-*/
-static int diva_strace_read_asc(diva_man_var_header_t *pVar, char *var) {
-       char *ptr = (char *)&pVar->path_length;
-
-       ptr += (pVar->path_length + 1);
-       memcpy(var, ptr + 1, *ptr);
-       var[(int)*ptr] = 0;
-
-       return (0);
-}
-
-/*
-  Read one information element - i.e. one string of byte values with
-  one length byte in front
-*/
-static int diva_strace_read_ie(diva_man_var_header_t *pVar,
-                              diva_trace_ie_t *var) {
-       char *ptr = (char *)&pVar->path_length;
-
-       ptr += (pVar->path_length + 1);
-
-       var->length = *ptr;
-       memcpy(&var->data[0], ptr + 1, *ptr);
-
-       return (0);
-}
-
-static int SuperTraceSetAudioTap(void *hLib, int Channel, int on) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if ((Channel < 1) || (Channel > pLib->Channels)) {
-               return (-1);
-       }
-       Channel--;
-
-       if (on) {
-               pLib->audio_tap_mask |=  (1L << Channel);
-       } else {
-               pLib->audio_tap_mask &= ~(1L << Channel);
-       }
-
-       /*
-         EYE patterns have TM_M_DATA set as additional
-         condition
-       */
-       if (pLib->audio_tap_mask) {
-               pLib->trace_event_mask |= TM_M_DATA;
-       } else {
-               pLib->trace_event_mask &= ~TM_M_DATA;
-       }
-
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceSetBChannel(void *hLib, int Channel, int on) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if ((Channel < 1) || (Channel > pLib->Channels)) {
-               return (-1);
-       }
-       Channel--;
-
-       if (on) {
-               pLib->bchannel_trace_mask |=  (1L << Channel);
-       } else {
-               pLib->bchannel_trace_mask &= ~(1L << Channel);
-       }
-
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceSetDChannel(void *hLib, int on) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if (on) {
-               pLib->trace_event_mask |= (TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
-       } else {
-               pLib->trace_event_mask &= ~(TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
-       }
-
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceSetInfo(void *hLib, int on) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if (on) {
-               pLib->trace_event_mask |= TM_STRING;
-       } else {
-               pLib->trace_event_mask &= ~TM_STRING;
-       }
-
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceClearCall(void *hLib, int Channel) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-
-       if ((Channel < 1) || (Channel > pLib->Channels)) {
-               return (-1);
-       }
-       Channel--;
-
-       pLib->clear_call_command |= (1L << Channel);
-
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-/*
-  Parse and update cumulative statistice
-*/
-static int diva_ifc_statistics(diva_strace_context_t *pLib,
-                              diva_man_var_header_t *pVar) {
-       diva_man_var_header_t *cur;
-       int i, one_updated = 0, mdm_updated = 0, fax_updated = 0;
-
-       for (i  = pLib->statistic_parse_first; i <= pLib->statistic_parse_last; i++) {
-               if ((cur = find_var(pVar, pLib->parse_table[i].path))) {
-                       if (diva_trace_read_variable(cur, pLib->parse_table[i].variable)) {
-                               diva_trace_error(pLib, -3 , __FILE__, __LINE__);
-                               return (-1);
-                       }
-                       one_updated = 1;
-                       if ((i >= pLib->mdm_statistic_parse_first) && (i <= pLib->mdm_statistic_parse_last)) {
-                               mdm_updated = 1;
-                       }
-                       if ((i >= pLib->fax_statistic_parse_first) && (i <= pLib->fax_statistic_parse_last)) {
-                               fax_updated = 1;
-                       }
-               }
-       }
-
-       /*
-         We do not use first event to notify user - this is the event that is
-         generated as result of EVENT ON operation and is used only to initialize
-         internal variables of application
-       */
-       if (mdm_updated) {
-               diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE);
-       } else if (fax_updated) {
-               diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE);
-       } else if (one_updated) {
-               diva_trace_notify_user(pLib, 0, DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE);
-       }
-
-       return (one_updated ? 0 : -1);
-}
-
-static int SuperTraceGetOutgoingCallStatistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->outgoing_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetIncomingCallStatistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->incoming_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetModemStatistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->modem_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetFaxStatistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->fax_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetBLayer1Statistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->b1_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetBLayer2Statistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->b2_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetDLayer1Statistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->d1_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-static int SuperTraceGetDLayer2Statistics(void *hLib) {
-       diva_strace_context_t *pLib = (diva_strace_context_t *)hLib;
-       pLib->d2_ifc_stats = 1;
-       return (ScheduleNextTraceRequest(pLib));
-}
-
-dword DivaSTraceGetMemotyRequirement(int channels) {
-       dword parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
-                              STAT_PARSE_ENTRIES + \
-                              LINE_PARSE_ENTRIES + 1) * channels;
-       return (sizeof(diva_strace_context_t) + \
-               (parse_entries * sizeof(diva_strace_path2action_t)));
-}
diff --git a/drivers/isdn/hardware/eicon/maintidi.h b/drivers/isdn/hardware/eicon/maintidi.h
deleted file mode 100644 (file)
index 2b46147..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2000.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    1.9
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_EICON_TRACE_IDI_IFC_H__
-#define __DIVA_EICON_TRACE_IDI_IFC_H__
-
-void *SuperTraceOpenAdapter(int AdapterNumber);
-int SuperTraceCloseAdapter(void *AdapterHandle);
-int SuperTraceWrite(void *AdapterHandle,
-                   const void *data, int length);
-int SuperTraceReadRequest(void *AdapterHandle, const char *name, byte *data);
-int SuperTraceGetNumberOfChannels(void *AdapterHandle);
-int SuperTraceASSIGN(void *AdapterHandle, byte *data);
-int SuperTraceREMOVE(void *AdapterHandle);
-int SuperTraceTraceOnRequest(void *hAdapter, const char *name, byte *data);
-int SuperTraceWriteVar(void *AdapterHandle,
-                      byte *data,
-                      const char *name,
-                      void *var,
-                      byte type,
-                      byte var_length);
-int SuperTraceExecuteRequest(void *AdapterHandle,
-                            const char *name,
-                            byte *data);
-
-typedef struct _diva_strace_path2action {
-       char path[64]; /* Full path to variable            */
-       void *variable; /* Variable that will receive value */
-} diva_strace_path2action_t;
-
-#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096
-
-typedef struct _diva_strace_context {
-       diva_strace_library_interface_t instance;
-
-       int Adapter;
-       void *hAdapter;
-
-       int Channels;
-       int req_busy;
-
-       ENTITY e;
-       IDI_CALL request;
-       BUFFERS XData;
-       BUFFERS RData;
-       byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1];
-       int removal_state;
-       int general_b_ch_event;
-       int general_fax_event;
-       int general_mdm_event;
-
-       byte rc_ok;
-
-       /*
-         Initialization request state machine
-       */
-       int ChannelsTraceActive;
-       int ModemTraceActive;
-       int FaxTraceActive;
-       int IncomingCallsCallsActive;
-       int IncomingCallsConnectedActive;
-       int OutgoingCallsCallsActive;
-       int OutgoingCallsConnectedActive;
-
-       int trace_mask_init;
-       int audio_trace_init;
-       int bchannel_init;
-       int trace_length_init;
-       int     trace_on;
-       int trace_events_down;
-       int l1_trace;
-       int l2_trace;
-
-       /*
-         Trace\Event Enable
-       */
-       word trace_event_mask;
-       word current_trace_event_mask;
-
-       dword audio_tap_mask;
-       dword current_audio_tap_mask;
-       dword current_eye_pattern_mask;
-       int   audio_tap_pending;
-       int   eye_pattern_pending;
-
-       dword bchannel_trace_mask;
-       dword current_bchannel_trace_mask;
-
-
-       diva_trace_line_state_t lines[30];
-
-       int     parse_entries;
-       int     cur_parse_entry;
-       diva_strace_path2action_t *parse_table;
-
-       diva_trace_library_user_interface_t user_proc_table;
-
-       int line_parse_entry_first[30];
-       int line_parse_entry_last[30];
-
-       int modem_parse_entry_first[30];
-       int modem_parse_entry_last[30];
-
-       int fax_parse_entry_first[30];
-       int fax_parse_entry_last[30];
-
-       int statistic_parse_first;
-       int statistic_parse_last;
-
-       int mdm_statistic_parse_first;
-       int mdm_statistic_parse_last;
-
-       int fax_statistic_parse_first;
-       int fax_statistic_parse_last;
-
-       dword   line_init_event;
-       dword   modem_init_event;
-       dword   fax_init_event;
-
-       dword   pending_line_status;
-       dword   pending_modem_status;
-       dword   pending_fax_status;
-
-       dword clear_call_command;
-
-       int outgoing_ifc_stats;
-       int incoming_ifc_stats;
-       int modem_ifc_stats;
-       int fax_ifc_stats;
-       int b1_ifc_stats;
-       int b2_ifc_stats;
-       int d1_ifc_stats;
-       int d2_ifc_stats;
-
-       diva_trace_interface_state_t Interface;
-       diva_ifc_statistics_t                            InterfaceStat;
-} diva_strace_context_t;
-
-typedef struct _diva_man_var_header {
-       byte   escape;
-       byte   length;
-       byte   management_id;
-       byte   type;
-       byte   attribute;
-       byte   status;
-       byte   value_length;
-       byte     path_length;
-} diva_man_var_header_t;
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/man_defs.h b/drivers/isdn/hardware/eicon/man_defs.h
deleted file mode 100644 (file)
index 249c471..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    1.9
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-/* Definitions for use with the Management Information Element      */
-
-/*------------------------------------------------------------------*/
-/* Management information element                                   */
-/* ----------------------------------------------------------       */
-/* Byte     Coding            Comment                               */
-/* ----------------------------------------------------------       */
-/*    0 | 0 1 1 1 1 1 1 1 | ESC                                     */
-/*    1 | 0 x x x x x x x | Length of information element (m-1)     */
-/*    2 | 1 0 0 0 0 0 0 0 | Management Information Id               */
-/*    3 | x x x x x x x x | Type                                    */
-/*    4 | x x x x x x x x | Attribute                               */
-/*    5 | x x x x x x x x | Status                                  */
-/*    6 | x x x x x x x x | Variable Value Length (m-n)             */
-/*    7 | x x x x x x x x | Path / Variable Name String Length (n-8)*/
-/* 8..n | x x x x x x x x | Path/Node Name String separated by '\'  */
-/* n..m | x x x x x x x x | Variable content                        */
-/*------------------------------------------------------------------*/
-
-/*------------------------------------------------------------------*/
-/* Type Field                                                       */
-/*                                                                  */
-/* MAN_READ:      not used                                          */
-/* MAN_WRITE:     not used                                          */
-/* MAN_EVENT_ON:  not used                                          */
-/* MAN_EVENT_OFF: not used                                          */
-/* MAN_INFO_IND:  type of variable                                  */
-/* MAN_EVENT_IND: type of variable                                  */
-/* MAN_TRACE_IND  not used                                          */
-/*------------------------------------------------------------------*/
-#define MI_DIR          0x01  /* Directory string (zero terminated) */
-#define MI_EXECUTE      0x02  /* Executable function (has no value) */
-#define MI_ASCIIZ       0x03  /* Zero terminated string             */
-#define MI_ASCII        0x04  /* String, first byte is length       */
-#define MI_NUMBER       0x05  /* Number string, first byte is length*/
-#define MI_TRACE        0x06  /* Trace information, format see below*/
-
-#define MI_FIXED_LENGTH 0x80  /* get length from MAN_INFO max_len   */
-#define MI_INT          0x81  /* number to display as signed int    */
-#define MI_UINT         0x82  /* number to display as unsigned int  */
-#define MI_HINT         0x83  /* number to display in hex format    */
-#define MI_HSTR         0x84  /* number to display as a hex string  */
-#define MI_BOOLEAN      0x85  /* number to display as boolean       */
-#define MI_IP_ADDRESS   0x86  /* number to display as IP address    */
-#define MI_BITFLD       0x87  /* number to display as bit field     */
-#define MI_SPID_STATE   0x88  /* state# of SPID initialisation      */
-
-/*------------------------------------------------------------------*/
-/* Attribute Field                                                  */
-/*                                                                  */
-/* MAN_READ:      not used                                          */
-/* MAN_WRITE:     not used                                          */
-/* MAN_EVENT_ON:  not used                                          */
-/* MAN_EVENT_OFF: not used                                          */
-/* MAN_INFO_IND:  set according to capabilities of that variable    */
-/* MAN_EVENT_IND: not used                                          */
-/* MAN_TRACE_IND  not used                                          */
-/*------------------------------------------------------------------*/
-#define MI_WRITE        0x01  /* Variable is writeable              */
-#define MI_EVENT        0x02  /* Variable can indicate changes      */
-
-/*------------------------------------------------------------------*/
-/* Status Field                                                     */
-/*                                                                  */
-/* MAN_READ:      not used                                          */
-/* MAN_WRITE:     not used                                          */
-/* MAN_EVENT_ON:  not used                                          */
-/* MAN_EVENT_OFF: not used                                          */
-/* MAN_INFO_IND:  set according to the actual status                */
-/* MAN_EVENT_IND: set according to the actual statu                 */
-/* MAN_TRACE_IND  not used                                          */
-/*------------------------------------------------------------------*/
-#define MI_LOCKED       0x01  /* write protected by another instance*/
-#define MI_EVENT_ON     0x02  /* Event logging switched on          */
-#define MI_PROTECTED    0x04  /* write protected by this instance   */
-
-/*------------------------------------------------------------------*/
-/* Data Format used for MAN_TRACE_IND (no MI-element used)          */
-/*------------------------------------------------------------------*/
-typedef struct mi_xlog_hdr_s MI_XLOG_HDR;
-struct mi_xlog_hdr_s
-{
-       unsigned long  time;   /* Timestamp in msec units                 */
-       unsigned short size;   /* Size of data that follows               */
-       unsigned short code;   /* code of trace event                     */
-};                       /* unspecified data follows this header    */
-
-/*------------------------------------------------------------------*/
-/* Trace mask definitions for trace events except B channel and     */
-/* debug trace events                                               */
-/*------------------------------------------------------------------*/
-#define TM_D_CHAN   0x0001  /* D-Channel        (D-.) Code 3,4      */
-#define TM_L_LAYER  0x0002  /* Low Layer        (LL)  Code 6,7      */
-#define TM_N_LAYER  0x0004  /* Network Layer    (N)   Code 14,15    */
-#define TM_DL_ERR   0x0008  /* Data Link Error  (MDL) Code 9        */
-#define TM_LAYER1   0x0010  /* Layer 1                Code 20       */
-#define TM_C_COMM   0x0020  /* Call Comment     (SIG) Code 5,21,22  */
-#define TM_M_DATA   0x0040  /* Modulation Data  (EYE) Code 23       */
-#define TM_STRING   0x0080  /* Sting data             Code 24       */
-#define TM_N_USED2  0x0100  /* not used                             */
-#define TM_N_USED3  0x0200  /* not used                             */
-#define TM_N_USED4  0x0400  /* not used                             */
-#define TM_N_USED5  0x0800  /* not used                             */
-#define TM_N_USED6  0x1000  /* not used                             */
-#define TM_N_USED7  0x2000  /* not used                             */
-#define TM_N_USED8  0x4000  /* not used                             */
-#define TM_REST     0x8000  /* Codes 10,11,12,13,16,18,19,128,129   */
-
-/*------ End of file -----------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mdm_msg.h b/drivers/isdn/hardware/eicon/mdm_msg.h
deleted file mode 100644 (file)
index 0e6b2e0..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __EICON_MDM_MSG_H__
-#define __EICON_MDM_MSG_H__
-#define DSP_UDATA_INDICATION_DCD_OFF  0x01
-#define DSP_UDATA_INDICATION_DCD_ON  0x02
-#define DSP_UDATA_INDICATION_CTS_OFF  0x03
-#define DSP_UDATA_INDICATION_CTS_ON  0x04
-/* =====================================================================
-   DCD_OFF Message:
-   <word> time of DCD off (sampled from counter at 8kHz)
-   DCD_ON Message:
-   <word> time of DCD on (sampled from counter at 8kHz)
-   <byte> connected norm
-   <word> connected options
-   <dword> connected speed (bit/s, max of tx and rx speed)
-   <word> roundtrip delay (ms)
-   <dword> connected speed tx (bit/s)
-   <dword> connected speed rx (bit/s)
-   Size of this message == 19 bytes, but we will receive only 11
-   ===================================================================== */
-#define DSP_CONNECTED_NORM_UNSPECIFIED      0
-#define DSP_CONNECTED_NORM_V21              1
-#define DSP_CONNECTED_NORM_V23              2
-#define DSP_CONNECTED_NORM_V22              3
-#define DSP_CONNECTED_NORM_V22_BIS          4
-#define DSP_CONNECTED_NORM_V32_BIS          5
-#define DSP_CONNECTED_NORM_V34              6
-#define DSP_CONNECTED_NORM_V8               7
-#define DSP_CONNECTED_NORM_BELL_212A        8
-#define DSP_CONNECTED_NORM_BELL_103         9
-#define DSP_CONNECTED_NORM_V29_LEASED_LINE  10
-#define DSP_CONNECTED_NORM_V33_LEASED_LINE  11
-#define DSP_CONNECTED_NORM_V90              12
-#define DSP_CONNECTED_NORM_V21_CH2          13
-#define DSP_CONNECTED_NORM_V27_TER          14
-#define DSP_CONNECTED_NORM_V29              15
-#define DSP_CONNECTED_NORM_V33              16
-#define DSP_CONNECTED_NORM_V17              17
-#define DSP_CONNECTED_NORM_V32              18
-#define DSP_CONNECTED_NORM_K56_FLEX         19
-#define DSP_CONNECTED_NORM_X2               20
-#define DSP_CONNECTED_NORM_V18              21
-#define DSP_CONNECTED_NORM_V18_LOW_HIGH     22
-#define DSP_CONNECTED_NORM_V18_HIGH_LOW     23
-#define DSP_CONNECTED_NORM_V21_LOW_HIGH     24
-#define DSP_CONNECTED_NORM_V21_HIGH_LOW     25
-#define DSP_CONNECTED_NORM_BELL103_LOW_HIGH 26
-#define DSP_CONNECTED_NORM_BELL103_HIGH_LOW 27
-#define DSP_CONNECTED_NORM_V23_75_1200      28
-#define DSP_CONNECTED_NORM_V23_1200_75      29
-#define DSP_CONNECTED_NORM_EDT_110          30
-#define DSP_CONNECTED_NORM_BAUDOT_45        31
-#define DSP_CONNECTED_NORM_BAUDOT_47        32
-#define DSP_CONNECTED_NORM_BAUDOT_50        33
-#define DSP_CONNECTED_NORM_DTMF             34
-#define DSP_CONNECTED_NORM_V18_RESERVED_13  35
-#define DSP_CONNECTED_NORM_V18_RESERVED_14  36
-#define DSP_CONNECTED_NORM_V18_RESERVED_15  37
-#define DSP_CONNECTED_NORM_VOWN             38
-#define DSP_CONNECTED_NORM_V23_OFF_HOOK     39
-#define DSP_CONNECTED_NORM_V23_ON_HOOK      40
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_3  41
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_4  42
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_5  43
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_6  44
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_7  45
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_8  46
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_9  47
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_10 48
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_11 49
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_12 50
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_13 51
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_14 52
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_15 53
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_16 54
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_17 55
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_18 56
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_19 57
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_20 58
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_21 59
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_22 60
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_23 61
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_24 62
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_25 63
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_26 64
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_27 65
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_28 66
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_29 67
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_30 68
-#define DSP_CONNECTED_NORM_VOWN_RESERVED_31 69
-#define DSP_CONNECTED_OPTION_TRELLIS             0x0001
-#define DSP_CONNECTED_OPTION_V42_TRANS           0x0002
-#define DSP_CONNECTED_OPTION_V42_LAPM            0x0004
-#define DSP_CONNECTED_OPTION_SHORT_TRAIN         0x0008
-#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
-#define DSP_CONNECTED_OPTION_V42BIS              0x0020
-#define DSP_CONNECTED_OPTION_MNP2                0x0040
-#define DSP_CONNECTED_OPTION_MNP3                0x0080
-#define DSP_CONNECTED_OPTION_MNP4                0x00c0
-#define DSP_CONNECTED_OPTION_MNP5                0x0100
-#define DSP_CONNECTED_OPTION_MNP10               0x0200
-#define DSP_CONNECTED_OPTION_MASK_V42            0x0024
-#define DSP_CONNECTED_OPTION_MASK_MNP            0x03c0
-#define DSP_CONNECTED_OPTION_MASK_ERROR_CORRECT  0x03e4
-#define DSP_CONNECTED_OPTION_MASK_COMPRESSION    0x0320
-#define DSP_UDATA_INDICATION_DISCONNECT         5
-/*
-  returns:
-  <byte> cause
-*/
-/* ==========================================================
-   DLC: B2 modem configuration
-   ========================================================== */
-/*
-  Fields in assign DLC information element for modem protocol V.42/MNP:
-  <byte> length of information element
-  <word> information field length
-  <byte> address A       (not used, default 3)
-  <byte> address B       (not used, default 1)
-  <byte> modulo mode     (not used, default 7)
-  <byte> window size     (not used, default 7)
-  <word> XID length      (not used, default 0)
-  ...    XID information (not used, default empty)
-  <byte> modem protocol negotiation options
-  <byte> modem protocol options
-  <byte> modem protocol break configuration
-  <byte> modem protocol application options
-*/
-#define DLC_MODEMPROT_DISABLE_V42_V42BIS     0x01
-#define DLC_MODEMPROT_DISABLE_MNP_MNP5       0x02
-#define DLC_MODEMPROT_REQUIRE_PROTOCOL       0x04
-#define DLC_MODEMPROT_DISABLE_V42_DETECT     0x08
-#define DLC_MODEMPROT_DISABLE_COMPRESSION    0x10
-#define DLC_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x20
-#define DLC_MODEMPROT_NO_PROTOCOL_IF_1200    0x01
-#define DLC_MODEMPROT_BUFFER_IN_V42_DETECT   0x02
-#define DLC_MODEMPROT_DISABLE_V42_SREJ       0x04
-#define DLC_MODEMPROT_DISABLE_MNP3           0x08
-#define DLC_MODEMPROT_DISABLE_MNP4           0x10
-#define DLC_MODEMPROT_DISABLE_MNP10          0x20
-#define DLC_MODEMPROT_NO_PROTOCOL_IF_V22BIS  0x40
-#define DLC_MODEMPROT_NO_PROTOCOL_IF_V32BIS  0x80
-#define DLC_MODEMPROT_BREAK_DISABLED         0x00
-#define DLC_MODEMPROT_BREAK_NORMAL           0x01
-#define DLC_MODEMPROT_BREAK_EXPEDITED        0x02
-#define DLC_MODEMPROT_BREAK_DESTRUCTIVE      0x03
-#define DLC_MODEMPROT_BREAK_CONFIG_MASK      0x03
-#define DLC_MODEMPROT_APPL_EARLY_CONNECT     0x01
-#define DLC_MODEMPROT_APPL_PASS_INDICATIONS  0x02
-/* ==========================================================
-   CAI parameters used for the modem L1 configuration
-   ========================================================== */
-/*
-  Fields in assign CAI information element:
-  <byte> length of information element
-  <byte> info field and B-channel hardware
-  <byte> rate adaptation bit rate
-  <byte> async framing parameters
-  <byte> reserved
-  <word> packet length
-  <byte> modem line taking options
-  <byte> modem modulation negotiation parameters
-  <byte> modem modulation options
-  <byte> modem disabled modulations mask low
-  <byte> modem disabled modulations mask high
-  <byte> modem enabled modulations mask
-  <word> modem min TX speed
-  <word> modem max TX speed
-  <word> modem min RX speed
-  <word> modem max RX speed
-  <byte> modem disabled symbol rates mask
-  <byte> modem info options mask
-  <byte> modem transmit level adjust
-  <byte> modem speaker parameters
-  <word> modem private debug config
-  <struct> modem reserved
-  <struct> v18 config parameters
-  <struct> v18 probing sequence
-  <struct> v18 probing message
-*/
-#define DSP_CAI_HARDWARE_HDLC_64K          0x05
-#define DSP_CAI_HARDWARE_HDLC_56K          0x08
-#define DSP_CAI_HARDWARE_TRANSP            0x09
-#define DSP_CAI_HARDWARE_V110_SYNC         0x0c
-#define DSP_CAI_HARDWARE_V110_ASYNC        0x0d
-#define DSP_CAI_HARDWARE_HDLC_128K         0x0f
-#define DSP_CAI_HARDWARE_FAX               0x10
-#define DSP_CAI_HARDWARE_MODEM_ASYNC       0x11
-#define DSP_CAI_HARDWARE_MODEM_SYNC        0x12
-#define DSP_CAI_HARDWARE_V110_HDLCA        0x13
-#define DSP_CAI_HARDWARE_ADVANCED_VOICE    0x14
-#define DSP_CAI_HARDWARE_TRANSP_DTMF       0x16
-#define DSP_CAI_HARDWARE_DTMF_VOICE_ISDN   0x17
-#define DSP_CAI_HARDWARE_DTMF_VOICE_LOCAL  0x18
-#define DSP_CAI_HARDWARE_MASK              0x3f
-#define DSP_CAI_ENABLE_INFO_INDICATIONS    0x80
-#define DSP_CAI_RATE_ADAPTATION_300        0x00
-#define DSP_CAI_RATE_ADAPTATION_600        0x01
-#define DSP_CAI_RATE_ADAPTATION_1200       0x02
-#define DSP_CAI_RATE_ADAPTATION_2400       0x03
-#define DSP_CAI_RATE_ADAPTATION_4800       0x04
-#define DSP_CAI_RATE_ADAPTATION_9600       0x05
-#define DSP_CAI_RATE_ADAPTATION_19200      0x06
-#define DSP_CAI_RATE_ADAPTATION_38400      0x07
-#define DSP_CAI_RATE_ADAPTATION_48000      0x08
-#define DSP_CAI_RATE_ADAPTATION_56000      0x09
-#define DSP_CAI_RATE_ADAPTATION_7200       0x0a
-#define DSP_CAI_RATE_ADAPTATION_14400      0x0b
-#define DSP_CAI_RATE_ADAPTATION_28800      0x0c
-#define DSP_CAI_RATE_ADAPTATION_12000      0x0d
-#define DSP_CAI_RATE_ADAPTATION_1200_75    0x0e
-#define DSP_CAI_RATE_ADAPTATION_75_1200    0x0f
-#define DSP_CAI_RATE_ADAPTATION_MASK       0x0f
-#define DSP_CAI_ASYNC_PARITY_ENABLE        0x01
-#define DSP_CAI_ASYNC_PARITY_SPACE         0x00
-#define DSP_CAI_ASYNC_PARITY_ODD           0x02
-#define DSP_CAI_ASYNC_PARITY_EVEN          0x04
-#define DSP_CAI_ASYNC_PARITY_MARK          0x06
-#define DSP_CAI_ASYNC_PARITY_MASK          0x06
-#define DSP_CAI_ASYNC_ONE_STOP_BIT         0x00
-#define DSP_CAI_ASYNC_TWO_STOP_BITS        0x20
-#define DSP_CAI_ASYNC_CHAR_LENGTH_8        0x00
-#define DSP_CAI_ASYNC_CHAR_LENGTH_7        0x40
-#define DSP_CAI_ASYNC_CHAR_LENGTH_6        0x80
-#define DSP_CAI_ASYNC_CHAR_LENGTH_5        0xc0
-#define DSP_CAI_ASYNC_CHAR_LENGTH_MASK     0xc0
-#define DSP_CAI_MODEM_LEASED_LINE_MODE     0x01
-#define DSP_CAI_MODEM_4_WIRE_OPERATION     0x02
-#define DSP_CAI_MODEM_DISABLE_BUSY_DETECT  0x04
-#define DSP_CAI_MODEM_DISABLE_CALLING_TONE 0x08
-#define DSP_CAI_MODEM_DISABLE_ANSWER_TONE  0x10
-#define DSP_CAI_MODEM_ENABLE_DIAL_TONE_DET 0x20
-#define DSP_CAI_MODEM_USE_POTS_INTERFACE   0x40
-#define DSP_CAI_MODEM_FORCE_RAY_TAYLOR_FAX 0x80
-#define DSP_CAI_MODEM_NEGOTIATE_HIGHEST    0x00
-#define DSP_CAI_MODEM_NEGOTIATE_DISABLED   0x01
-#define DSP_CAI_MODEM_NEGOTIATE_IN_CLASS   0x02
-#define DSP_CAI_MODEM_NEGOTIATE_V100       0x03
-#define DSP_CAI_MODEM_NEGOTIATE_V8         0x04
-#define DSP_CAI_MODEM_NEGOTIATE_V8BIS      0x05
-#define DSP_CAI_MODEM_NEGOTIATE_MASK       0x07
-#define DSP_CAI_MODEM_GUARD_TONE_NONE      0x00
-#define DSP_CAI_MODEM_GUARD_TONE_550HZ     0x40
-#define DSP_CAI_MODEM_GUARD_TONE_1800HZ    0x80
-#define DSP_CAI_MODEM_GUARD_TONE_MASK      0xc0
-#define DSP_CAI_MODEM_DISABLE_RETRAIN      0x01
-#define DSP_CAI_MODEM_DISABLE_STEPUPDOWN   0x02
-#define DSP_CAI_MODEM_DISABLE_SPLIT_SPEED  0x04
-#define DSP_CAI_MODEM_DISABLE_TRELLIS      0x08
-#define DSP_CAI_MODEM_ALLOW_RDL_TEST_LOOP  0x10
-#define DSP_CAI_MODEM_DISABLE_FLUSH_TIMER  0x40
-#define DSP_CAI_MODEM_REVERSE_DIRECTION    0x80
-#define DSP_CAI_MODEM_DISABLE_V21          0x01
-#define DSP_CAI_MODEM_DISABLE_V23          0x02
-#define DSP_CAI_MODEM_DISABLE_V22          0x04
-#define DSP_CAI_MODEM_DISABLE_V22BIS       0x08
-#define DSP_CAI_MODEM_DISABLE_V32          0x10
-#define DSP_CAI_MODEM_DISABLE_V32BIS       0x20
-#define DSP_CAI_MODEM_DISABLE_V34          0x40
-#define DSP_CAI_MODEM_DISABLE_V90          0x80
-#define DSP_CAI_MODEM_DISABLE_BELL103      0x01
-#define DSP_CAI_MODEM_DISABLE_BELL212A     0x02
-#define DSP_CAI_MODEM_DISABLE_VFC          0x04
-#define DSP_CAI_MODEM_DISABLE_K56FLEX      0x08
-#define DSP_CAI_MODEM_DISABLE_X2           0x10
-#define DSP_CAI_MODEM_ENABLE_V29FDX        0x01
-#define DSP_CAI_MODEM_ENABLE_V33           0x02
-#define DSP_CAI_MODEM_DISABLE_2400_SYMBOLS 0x01
-#define DSP_CAI_MODEM_DISABLE_2743_SYMBOLS 0x02
-#define DSP_CAI_MODEM_DISABLE_2800_SYMBOLS 0x04
-#define DSP_CAI_MODEM_DISABLE_3000_SYMBOLS 0x08
-#define DSP_CAI_MODEM_DISABLE_3200_SYMBOLS 0x10
-#define DSP_CAI_MODEM_DISABLE_3429_SYMBOLS 0x20
-#define DSP_CAI_MODEM_DISABLE_TX_REDUCTION 0x01
-#define DSP_CAI_MODEM_DISABLE_PRECODING    0x02
-#define DSP_CAI_MODEM_DISABLE_PREEMPHASIS  0x04
-#define DSP_CAI_MODEM_DISABLE_SHAPING      0x08
-#define DSP_CAI_MODEM_DISABLE_NONLINEAR_EN 0x10
-#define DSP_CAI_MODEM_SPEAKER_OFF          0x00
-#define DSP_CAI_MODEM_SPEAKER_DURING_TRAIN 0x01
-#define DSP_CAI_MODEM_SPEAKER_TIL_CONNECT  0x02
-#define DSP_CAI_MODEM_SPEAKER_ALWAYS_ON    0x03
-#define DSP_CAI_MODEM_SPEAKER_CONTROL_MASK 0x03
-#define DSP_CAI_MODEM_SPEAKER_VOLUME_MIN   0x00
-#define DSP_CAI_MODEM_SPEAKER_VOLUME_LOW   0x04
-#define DSP_CAI_MODEM_SPEAKER_VOLUME_HIGH  0x08
-#define DSP_CAI_MODEM_SPEAKER_VOLUME_MAX   0x0c
-#define DSP_CAI_MODEM_SPEAKER_VOLUME_MASK  0x0c
-/* ==========================================================
-   DCD/CTS State
-   ========================================================== */
-#define MDM_WANT_CONNECT_B3_ACTIVE_I  0x01
-#define MDM_NCPI_VALID                0x02
-#define MDM_NCPI_CTS_ON_RECEIVED      0x04
-#define MDM_NCPI_DCD_ON_RECEIVED      0x08
-/* ==========================================================
-   CAPI NCPI Constants
-   ========================================================== */
-#define MDM_NCPI_ECM_V42              0x0001
-#define MDM_NCPI_ECM_MNP              0x0002
-#define MDM_NCPI_TRANSPARENT          0x0004
-#define MDM_NCPI_COMPRESSED           0x0010
-/* ==========================================================
-   CAPI B2 Config Constants
-   ========================================================== */
-#define MDM_B2_DISABLE_V42bis         0x0001
-#define MDM_B2_DISABLE_MNP            0x0002
-#define MDM_B2_DISABLE_TRANS          0x0004
-#define MDM_B2_DISABLE_V42            0x0008
-#define MDM_B2_DISABLE_COMP           0x0010
-/* ==========================================================
-   CAPI B1 Config Constants
-   ========================================================== */
-#define MDM_CAPI_DISABLE_RETRAIN      0x0001
-#define MDM_CAPI_DISABLE_RING_TONE    0x0002
-#define MDM_CAPI_GUARD_1800           0x0004
-#define MDM_CAPI_GUARD_550            0x0008
-#define MDM_CAPI_NEG_V8               0x0003
-#define MDM_CAPI_NEG_V100             0x0002
-#define MDM_CAPI_NEG_MOD_CLASS        0x0001
-#define MDM_CAPI_NEG_DISABLED         0x0000
-#endif
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
deleted file mode 100644 (file)
index def7992..0000000
+++ /dev/null
@@ -1,14954 +0,0 @@
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/bitmap.h>
-
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "capi20.h"
-#include "divacapi.h"
-#include "mdm_msg.h"
-#include "divasync.h"
-
-#define FILE_ "MESSAGE.C"
-#define dprintf
-
-/*------------------------------------------------------------------*/
-/* This is options supported for all adapters that are server by    */
-/* XDI driver. Allo it is not necessary to ask it from every adapter*/
-/* and it is not necessary to save it separate for every adapter    */
-/* Macrose defined here have only local meaning                     */
-/*------------------------------------------------------------------*/
-static dword diva_xdi_extended_features = 0;
-
-#define DIVA_CAPI_USE_CMA                 0x00000001
-#define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR  0x00000002
-#define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL  0x00000004
-#define DIVA_CAPI_XDI_PROVIDES_RX_DMA     0x00000008
-
-/*
-  CAPI can request to process all return codes self only if:
-  protocol code supports this && xdi supports this
-*/
-#define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__)   (((__a__)->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL) && ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) && (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL))
-
-/*------------------------------------------------------------------*/
-/* local function prototypes                                        */
-/*------------------------------------------------------------------*/
-
-static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci);
-void AutomaticLaw(DIVA_CAPI_ADAPTER *);
-word CapiRelease(word);
-word CapiRegister(word);
-word api_put(APPL *, CAPI_MSG *);
-static word api_parse(byte *, word, byte *, API_PARSE *);
-static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out);
-static void api_load_msg(API_SAVE *in, API_PARSE *out);
-
-word api_remove_start(void);
-void api_remove_complete(void);
-
-static void plci_remove(PLCI *);
-static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a);
-static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *, IDI_SYNC_REQ *);
-
-void callback(ENTITY *);
-
-static void control_rc(PLCI *, byte, byte, byte, byte, byte);
-static void data_rc(PLCI *, byte);
-static void data_ack(PLCI *, byte);
-static void sig_ind(PLCI *);
-static void SendInfo(PLCI *, dword, byte **, byte);
-static void SendSetupInfo(APPL *, PLCI *, dword, byte **, byte);
-static void SendSSExtInd(APPL *, PLCI *plci, dword Id, byte **parms);
-
-static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms);
-
-static void nl_ind(PLCI *);
-
-static byte connect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte listen_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte info_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte info_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte alert_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte facility_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte facility_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-
-static word get_plci(DIVA_CAPI_ADAPTER *);
-static void add_p(PLCI *, byte, byte *);
-static void add_s(PLCI *plci, byte code, API_PARSE *p);
-static void add_ss(PLCI *plci, byte code, API_PARSE *p);
-static void add_ie(PLCI *plci, byte code, byte *p, word p_length);
-static void add_d(PLCI *, word, byte *);
-static void add_ai(PLCI *, API_PARSE *);
-static word add_b1(PLCI *, API_PARSE *, word, word);
-static word add_b23(PLCI *, API_PARSE *);
-static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms);
-static void sig_req(PLCI *, byte, byte);
-static void nl_req_ncci(PLCI *, byte, byte);
-static void send_req(PLCI *);
-static void send_data(PLCI *);
-static word plci_remove_check(PLCI *);
-static void listen_check(DIVA_CAPI_ADAPTER *);
-static byte AddInfo(byte **, byte **, byte *, byte *);
-static byte getChannel(API_PARSE *);
-static void IndParse(PLCI *, const word *, byte **, byte);
-static byte ie_compare(byte *, byte *);
-static word find_cip(DIVA_CAPI_ADAPTER *, byte *, byte *);
-static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *, word);
-
-/*
-  XON protocol helpers
-*/
-static void channel_flow_control_remove(PLCI *plci);
-static void channel_x_off(PLCI *plci, byte ch, byte flag);
-static void channel_x_on(PLCI *plci, byte ch);
-static void channel_request_xon(PLCI *plci, byte ch);
-static void channel_xmit_xon(PLCI *plci);
-static int channel_can_xon(PLCI *plci, byte ch);
-static void channel_xmit_extended_xon(PLCI *plci);
-
-static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type, dword info_mask, byte setupParse);
-static word AdvCodecSupport(DIVA_CAPI_ADAPTER *, PLCI *, APPL *, byte);
-static void CodecIdCheck(DIVA_CAPI_ADAPTER *, PLCI *);
-static void SetVoiceChannel(PLCI *, byte *, DIVA_CAPI_ADAPTER *);
-static void VoiceChannelOff(PLCI *plci);
-static void adv_voice_write_coefs(PLCI *plci, word write_command);
-static void adv_voice_clear_config(PLCI *plci);
-
-static word get_b1_facilities(PLCI *plci, byte b1_resource);
-static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities);
-static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities);
-static word adjust_b_process(dword Id, PLCI *plci, byte Rc);
-static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command);
-static void adjust_b_restore(dword Id, PLCI *plci, byte Rc);
-static void reset_b3_command(dword Id, PLCI *plci, byte Rc);
-static void select_b_command(dword Id, PLCI *plci, byte Rc);
-static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc);
-static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc);
-static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc);
-static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc);
-static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc);
-static void hold_save_command(dword Id, PLCI *plci, byte Rc);
-static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc);
-static void init_b1_config(PLCI *plci);
-static void clear_b1_config(PLCI *plci);
-
-static void dtmf_command(dword Id, PLCI *plci, byte Rc);
-static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
-static void dtmf_confirmation(dword Id, PLCI *plci);
-static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length);
-static void dtmf_parameter_write(PLCI *plci);
-
-
-static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id);
-static void mixer_set_bchannel_id(PLCI *plci, byte *chi);
-static void mixer_clear_config(PLCI *plci);
-static void mixer_notify_update(PLCI *plci, byte others);
-static void mixer_command(dword Id, PLCI *plci, byte Rc);
-static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
-static void mixer_indication_coefs_set(dword Id, PLCI *plci);
-static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length);
-static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length);
-static void mixer_remove(PLCI *plci);
-
-
-static void ec_command(dword Id, PLCI *plci, byte Rc);
-static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
-static void ec_indication(dword Id, PLCI *plci, byte *msg, word length);
-
-
-static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc);
-static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc);
-
-
-static int diva_get_dma_descriptor(PLCI *plci, dword *dma_magic);
-static void diva_free_dma_descriptor(PLCI *plci, int nr);
-
-/*------------------------------------------------------------------*/
-/* external function prototypes                                     */
-/*------------------------------------------------------------------*/
-
-extern byte MapController(byte);
-extern byte UnMapController(byte);
-#define MapId(Id)(((Id) & 0xffffff00L) | MapController((byte)(Id)))
-#define UnMapId(Id)(((Id) & 0xffffff00L) | UnMapController((byte)(Id)))
-
-void sendf(APPL *, word, dword, word, byte *, ...);
-void *TransmitBufferSet(APPL *appl, dword ref);
-void *TransmitBufferGet(APPL *appl, void *p);
-void TransmitBufferFree(APPL *appl, void *p);
-void *ReceiveBufferGet(APPL *appl, int Num);
-
-int fax_head_line_time(char *buffer);
-
-
-/*------------------------------------------------------------------*/
-/* Global data definitions                                          */
-/*------------------------------------------------------------------*/
-extern byte max_adapter;
-extern byte max_appl;
-extern DIVA_CAPI_ADAPTER *adapter;
-extern APPL *application;
-
-
-
-
-
-
-
-static byte remove_started = false;
-static PLCI dummy_plci;
-
-
-static struct _ftable {
-       word command;
-       byte *format;
-       byte (*function)(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
-} ftable[] = {
-       {_DATA_B3_R,                          "dwww",         data_b3_req},
-       {_DATA_B3_I | RESPONSE,               "w",            data_b3_res},
-       {_INFO_R,                             "ss",           info_req},
-       {_INFO_I | RESPONSE,                  "",             info_res},
-       {_CONNECT_R,                          "wsssssssss",   connect_req},
-       {_CONNECT_I | RESPONSE,               "wsssss",       connect_res},
-       {_CONNECT_ACTIVE_I | RESPONSE,        "",             connect_a_res},
-       {_DISCONNECT_R,                       "s",            disconnect_req},
-       {_DISCONNECT_I | RESPONSE,            "",             disconnect_res},
-       {_LISTEN_R,                           "dddss",        listen_req},
-       {_ALERT_R,                            "s",            alert_req},
-       {_FACILITY_R,                         "ws",           facility_req},
-       {_FACILITY_I | RESPONSE,              "ws",           facility_res},
-       {_CONNECT_B3_R,                       "s",            connect_b3_req},
-       {_CONNECT_B3_I | RESPONSE,            "ws",           connect_b3_res},
-       {_CONNECT_B3_ACTIVE_I | RESPONSE,     "",             connect_b3_a_res},
-       {_DISCONNECT_B3_R,                    "s",            disconnect_b3_req},
-       {_DISCONNECT_B3_I | RESPONSE,         "",             disconnect_b3_res},
-       {_RESET_B3_R,                         "s",            reset_b3_req},
-       {_RESET_B3_I | RESPONSE,              "",             reset_b3_res},
-       {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "ws",           connect_b3_t90_a_res},
-       {_CONNECT_B3_T90_ACTIVE_I | RESPONSE, "",             connect_b3_t90_a_res},
-       {_SELECT_B_REQ,                       "s",            select_b_req},
-       {_MANUFACTURER_R,                     "dws",          manufacturer_req},
-       {_MANUFACTURER_I | RESPONSE,          "dws",          manufacturer_res},
-       {_MANUFACTURER_I | RESPONSE,          "",             manufacturer_res}
-};
-
-static byte *cip_bc[29][2] = {
-       { "",                     ""                     }, /* 0 */
-       { "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 1 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 2 */
-       { "\x02\x89\x90",         "\x02\x89\x90"         }, /* 3 */
-       { "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 4 */
-       { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 5 */
-       { "\x02\x98\x90",         "\x02\x98\x90"         }, /* 6 */
-       { "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */
-       { "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */
-       { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 9 */
-       { "",                     ""                     }, /* 10 */
-       { "",                     ""                     }, /* 11 */
-       { "",                     ""                     }, /* 12 */
-       { "",                     ""                     }, /* 13 */
-       { "",                     ""                     }, /* 14 */
-       { "",                     ""                     }, /* 15 */
-
-       { "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 16 */
-       { "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 17 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 18 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 19 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 20 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 21 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 22 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 23 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 24 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 25 */
-       { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 26 */
-       { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 27 */
-       { "\x02\x88\x90",         "\x02\x88\x90"         }  /* 28 */
-};
-
-static byte *cip_hlc[29] = {
-       "",                           /* 0 */
-       "",                           /* 1 */
-       "",                           /* 2 */
-       "",                           /* 3 */
-       "",                           /* 4 */
-       "",                           /* 5 */
-       "",                           /* 6 */
-       "",                           /* 7 */
-       "",                           /* 8 */
-       "",                           /* 9 */
-       "",                           /* 10 */
-       "",                           /* 11 */
-       "",                           /* 12 */
-       "",                           /* 13 */
-       "",                           /* 14 */
-       "",                           /* 15 */
-
-       "\x02\x91\x81",               /* 16 */
-       "\x02\x91\x84",               /* 17 */
-       "\x02\x91\xa1",               /* 18 */
-       "\x02\x91\xa4",               /* 19 */
-       "\x02\x91\xa8",               /* 20 */
-       "\x02\x91\xb1",               /* 21 */
-       "\x02\x91\xb2",               /* 22 */
-       "\x02\x91\xb5",               /* 23 */
-       "\x02\x91\xb8",               /* 24 */
-       "\x02\x91\xc1",               /* 25 */
-       "\x02\x91\x81",               /* 26 */
-       "\x03\x91\xe0\x01",           /* 27 */
-       "\x03\x91\xe0\x02"            /* 28 */
-};
-
-/*------------------------------------------------------------------*/
-
-#define V120_HEADER_LENGTH 1
-#define V120_HEADER_EXTEND_BIT  0x80
-#define V120_HEADER_BREAK_BIT   0x40
-#define V120_HEADER_C1_BIT      0x04
-#define V120_HEADER_C2_BIT      0x08
-#define V120_HEADER_FLUSH_COND  (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT)
-
-static byte v120_default_header[] =
-{
-
-       0x83                          /*  Ext, BR , res, res, C2 , C1 , B  , F   */
-
-};
-
-static byte v120_break_header[] =
-{
-
-       0xc3 | V120_HEADER_BREAK_BIT  /*  Ext, BR , res, res, C2 , C1 , B  , F   */
-
-};
-
-
-/*------------------------------------------------------------------*/
-/* API_PUT function                                                 */
-/*------------------------------------------------------------------*/
-
-word api_put(APPL *appl, CAPI_MSG *msg)
-{
-       word i, j, k, l, n;
-       word ret;
-       byte c;
-       byte controller;
-       DIVA_CAPI_ADAPTER *a;
-       PLCI *plci;
-       NCCI *ncci_ptr;
-       word ncci;
-       CAPI_MSG *m;
-       API_PARSE msg_parms[MAX_MSG_PARMS + 1];
-
-       if (msg->header.length < sizeof(msg->header) ||
-           msg->header.length > MAX_MSG_SIZE) {
-               dbug(1, dprintf("bad len"));
-               return _BAD_MSG;
-       }
-
-       controller = (byte)((msg->header.controller & 0x7f) - 1);
-
-       /* controller starts with 0 up to (max_adapter - 1) */
-       if (controller >= max_adapter)
-       {
-               dbug(1, dprintf("invalid ctrl"));
-               return _BAD_MSG;
-       }
-
-       a = &adapter[controller];
-       plci = NULL;
-       if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled)
-       {
-               dbug(1, dprintf("plci=%x", msg->header.plci));
-               plci = &a->plci[msg->header.plci - 1];
-               ncci = GET_WORD(&msg->header.ncci);
-               if (plci->Id
-                   && (plci->appl
-                       || (plci->State == INC_CON_PENDING)
-                       || (plci->State == INC_CON_ALERT)
-                       || (msg->header.command == (_DISCONNECT_I | RESPONSE)))
-                   && ((ncci == 0)
-                       || (msg->header.command == (_DISCONNECT_B3_I | RESPONSE))
-                       || ((ncci < MAX_NCCI + 1) && (a->ncci_plci[ncci] == plci->Id))))
-               {
-                       i = plci->msg_in_read_pos;
-                       j = plci->msg_in_write_pos;
-                       if (j >= i)
-                       {
-                               if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE)
-                                       i += MSG_IN_QUEUE_SIZE - j;
-                               else
-                                       j = 0;
-                       }
-                       else
-                       {
-
-                               n = (((CAPI_MSG *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc;
-
-                               if (i > MSG_IN_QUEUE_SIZE - n)
-                                       i = MSG_IN_QUEUE_SIZE - n + 1;
-                               i -= j;
-                       }
-
-                       if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc))
-
-                       {
-                               dbug(0, dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d",
-                                               msg->header.length, plci->msg_in_write_pos,
-                                               plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
-
-                               return _QUEUE_FULL;
-                       }
-                       c = false;
-                       if ((((byte *) msg) < ((byte *)(plci->msg_in_queue)))
-                           || (((byte *) msg) >= ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
-                       {
-                               if (plci->msg_in_write_pos != plci->msg_in_read_pos)
-                                       c = true;
-                       }
-                       if (msg->header.command == _DATA_B3_R)
-                       {
-                               if (msg->header.length < 20)
-                               {
-                                       dbug(1, dprintf("DATA_B3 REQ wrong length %d", msg->header.length));
-                                       return _BAD_MSG;
-                               }
-                               ncci_ptr = &(a->ncci[ncci]);
-                               n = ncci_ptr->data_pending;
-                               l = ncci_ptr->data_ack_pending;
-                               k = plci->msg_in_read_pos;
-                               while (k != plci->msg_in_write_pos)
-                               {
-                                       if (k == plci->msg_in_wrap_pos)
-                                               k = 0;
-                                       if ((((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R)
-                                           && (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.ncci == ncci))
-                                       {
-                                               n++;
-                                               if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004)
-                                                       l++;
-                                       }
-
-                                       k += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.length +
-                                             MSG_IN_OVERHEAD + 3) & 0xfffc;
-
-                               }
-                               if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK))
-                               {
-                                       dbug(0, dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d",
-                                                       ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l));
-
-                                       return _QUEUE_FULL;
-                               }
-                               if (plci->req_in || plci->internal_command)
-                               {
-                                       if ((((byte *) msg) >= ((byte *)(plci->msg_in_queue)))
-                                           && (((byte *) msg) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
-                                       {
-                                               dbug(0, dprintf("Q-FULL3(requeue)"));
-
-                                               return _QUEUE_FULL;
-                                       }
-                                       c = true;
-                               }
-                       }
-                       else
-                       {
-                               if (plci->req_in || plci->internal_command)
-                                       c = true;
-                               else
-                               {
-                                       plci->command = msg->header.command;
-                                       plci->number = msg->header.number;
-                               }
-                       }
-                       if (c)
-                       {
-                               dbug(1, dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d",
-                                               msg->header.command, plci->req_in, plci->internal_command,
-                                               msg->header.length, plci->msg_in_write_pos,
-                                               plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
-                               if (j == 0)
-                                       plci->msg_in_wrap_pos = plci->msg_in_write_pos;
-                               m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
-                               for (i = 0; i < msg->header.length; i++)
-                                       ((byte *)(plci->msg_in_queue))[j++] = ((byte *) msg)[i];
-                               if (m->header.command == _DATA_B3_R)
-                               {
-
-                                       m->info.data_b3_req.Data = (dword)(long)(TransmitBufferSet(appl, m->info.data_b3_req.Data));
-
-                               }
-
-                               j = (j + 3) & 0xfffc;
-
-                               *((APPL **)(&((byte *)(plci->msg_in_queue))[j])) = appl;
-                               plci->msg_in_write_pos = j + MSG_IN_OVERHEAD;
-                               return 0;
-                       }
-               }
-               else
-               {
-                       plci = NULL;
-               }
-       }
-       dbug(1, dprintf("com=%x", msg->header.command));
-
-       for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0;
-       for (i = 0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) {
-
-               if (ftable[i].command == msg->header.command) {
-                       /* break loop if the message is correct, otherwise continue scan  */
-                       /* (for example: CONNECT_B3_T90_ACT_RES has two specifications)   */
-                       if (!api_parse(msg->info.b, (word)(msg->header.length - 12), ftable[i].format, msg_parms)) {
-                               ret = 0;
-                               break;
-                       }
-                       for (j = 0; j < MAX_MSG_PARMS + 1; j++) msg_parms[j].length = 0;
-               }
-       }
-       if (ret) {
-               dbug(1, dprintf("BAD_MSG"));
-               if (plci) plci->command = 0;
-               return ret;
-       }
-
-
-       c = ftable[i].function(GET_DWORD(&msg->header.controller),
-                              msg->header.number,
-                              a,
-                              plci,
-                              appl,
-                              msg_parms);
-
-       channel_xmit_extended_xon(plci);
-
-       if (c == 1) send_req(plci);
-       if (c == 2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0;
-       if (plci && !plci->req_in) plci->command = 0;
-       return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/* api_parse function, check the format of api messages             */
-/*------------------------------------------------------------------*/
-
-static word api_parse(byte *msg, word length, byte *format, API_PARSE *parms)
-{
-       word i;
-       word p;
-
-       for (i = 0, p = 0; format[i]; i++) {
-               if (parms)
-               {
-                       parms[i].info = &msg[p];
-               }
-               switch (format[i]) {
-               case 'b':
-                       p += 1;
-                       break;
-               case 'w':
-                       p += 2;
-                       break;
-               case 'd':
-                       p += 4;
-                       break;
-               case 's':
-                       if (msg[p] == 0xff) {
-                               parms[i].info += 2;
-                               parms[i].length = msg[p + 1] + (msg[p + 2] << 8);
-                               p += (parms[i].length + 3);
-                       }
-                       else {
-                               parms[i].length = msg[p];
-                               p += (parms[i].length + 1);
-                       }
-                       break;
-               }
-
-               if (p > length) return true;
-       }
-       if (parms) parms[i].info = NULL;
-       return false;
-}
-
-static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
-{
-       word i, j, n = 0;
-       byte *p;
-
-       p = out->info;
-       for (i = 0; format[i] != '\0'; i++)
-       {
-               out->parms[i].info = p;
-               out->parms[i].length = in[i].length;
-               switch (format[i])
-               {
-               case 'b':
-                       n = 1;
-                       break;
-               case 'w':
-                       n = 2;
-                       break;
-               case 'd':
-                       n = 4;
-                       break;
-               case 's':
-                       n = in[i].length + 1;
-                       break;
-               }
-               for (j = 0; j < n; j++)
-                       *(p++) = in[i].info[j];
-       }
-       out->parms[i].info = NULL;
-       out->parms[i].length = 0;
-}
-
-static void api_load_msg(API_SAVE *in, API_PARSE *out)
-{
-       word i;
-
-       i = 0;
-       do
-       {
-               out[i].info = in->parms[i].info;
-               out[i].length = in->parms[i].length;
-       } while (in->parms[i++].info);
-}
-
-
-/*------------------------------------------------------------------*/
-/* CAPI remove function                                             */
-/*------------------------------------------------------------------*/
-
-word api_remove_start(void)
-{
-       word i;
-       word j;
-
-       if (!remove_started) {
-               remove_started = true;
-               for (i = 0; i < max_adapter; i++) {
-                       if (adapter[i].request) {
-                               for (j = 0; j < adapter[i].max_plci; j++) {
-                                       if (adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]);
-                               }
-                       }
-               }
-               return 1;
-       }
-       else {
-               for (i = 0; i < max_adapter; i++) {
-                       if (adapter[i].request) {
-                               for (j = 0; j < adapter[i].max_plci; j++) {
-                                       if (adapter[i].plci[j].Sig.Id) return 1;
-                               }
-                       }
-               }
-       }
-       api_remove_complete();
-       return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/* internal command queue                                           */
-/*------------------------------------------------------------------*/
-
-static void init_internal_command_queue(PLCI *plci)
-{
-       word i;
-
-       dbug(1, dprintf("%s,%d: init_internal_command_queue",
-                       (char *)(FILE_), __LINE__));
-
-       plci->internal_command = 0;
-       for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++)
-               plci->internal_command_queue[i] = NULL;
-}
-
-
-static void start_internal_command(dword Id, PLCI *plci, t_std_internal_command command_function)
-{
-       word i;
-
-       dbug(1, dprintf("[%06lx] %s,%d: start_internal_command",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       if (plci->internal_command == 0)
-       {
-               plci->internal_command_queue[0] = command_function;
-               (*command_function)(Id, plci, OK);
-       }
-       else
-       {
-               i = 1;
-               while (plci->internal_command_queue[i] != NULL)
-                       i++;
-               plci->internal_command_queue[i] = command_function;
-       }
-}
-
-
-static void next_internal_command(dword Id, PLCI *plci)
-{
-       word i;
-
-       dbug(1, dprintf("[%06lx] %s,%d: next_internal_command",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       plci->internal_command = 0;
-       plci->internal_command_queue[0] = NULL;
-       while (plci->internal_command_queue[1] != NULL)
-       {
-               for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++)
-                       plci->internal_command_queue[i] = plci->internal_command_queue[i + 1];
-               plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL;
-               (*(plci->internal_command_queue[0]))(Id, plci, OK);
-               if (plci->internal_command != 0)
-                       return;
-               plci->internal_command_queue[0] = NULL;
-       }
-}
-
-
-/*------------------------------------------------------------------*/
-/* NCCI allocate/remove function                                    */
-/*------------------------------------------------------------------*/
-
-static dword ncci_mapping_bug = 0;
-
-static word get_ncci(PLCI *plci, byte ch, word force_ncci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word ncci, i, j, k;
-
-       a = plci->adapter;
-       if (!ch || a->ch_ncci[ch])
-       {
-               ncci_mapping_bug++;
-               dbug(1, dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x",
-                               ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch]));
-               ncci = ch;
-       }
-       else
-       {
-               if (force_ncci)
-                       ncci = force_ncci;
-               else
-               {
-                       if ((ch < MAX_NCCI + 1) && !a->ncci_ch[ch])
-                               ncci = ch;
-                       else
-                       {
-                               ncci = 1;
-                               while ((ncci < MAX_NCCI + 1) && a->ncci_ch[ncci])
-                                       ncci++;
-                               if (ncci == MAX_NCCI + 1)
-                               {
-                                       ncci_mapping_bug++;
-                                       i = 1;
-                                       do
-                                       {
-                                               j = 1;
-                                               while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i))
-                                                       j++;
-                                               k = j;
-                                               if (j < MAX_NCCI + 1)
-                                               {
-                                                       do
-                                                       {
-                                                               j++;
-                                                       } while ((j < MAX_NCCI + 1) && (a->ncci_ch[j] != i));
-                                               }
-                                       } while ((i < MAX_NL_CHANNEL + 1) && (j < MAX_NCCI + 1));
-                                       if (i < MAX_NL_CHANNEL + 1)
-                                       {
-                                               dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x",
-                                                               ncci_mapping_bug, ch, force_ncci, i, k, j));
-                                       }
-                                       else
-                                       {
-                                               dbug(1, dprintf("NCCI mapping overflow %ld %02x %02x",
-                                                               ncci_mapping_bug, ch, force_ncci));
-                                       }
-                                       ncci = ch;
-                               }
-                       }
-                       a->ncci_plci[ncci] = plci->Id;
-                       a->ncci_state[ncci] = IDLE;
-                       if (!plci->ncci_ring_list)
-                               plci->ncci_ring_list = ncci;
-                       else
-                               a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list];
-                       a->ncci_next[plci->ncci_ring_list] = (byte) ncci;
-               }
-               a->ncci_ch[ncci] = ch;
-               a->ch_ncci[ch] = (byte) ncci;
-               dbug(1, dprintf("NCCI mapping established %ld %02x %02x %02x-%02x",
-                               ncci_mapping_bug, ch, force_ncci, ch, ncci));
-       }
-       return (ncci);
-}
-
-
-static void ncci_free_receive_buffers(PLCI *plci, word ncci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       APPL *appl;
-       word i, ncci_code;
-       dword Id;
-
-       a = plci->adapter;
-       Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
-       if (ncci)
-       {
-               if (a->ncci_plci[ncci] == plci->Id)
-               {
-                       if (!plci->appl)
-                       {
-                               ncci_mapping_bug++;
-                               dbug(1, dprintf("NCCI mapping appl expected %ld %08lx",
-                                               ncci_mapping_bug, Id));
-                       }
-                       else
-                       {
-                               appl = plci->appl;
-                               ncci_code = ncci | (((word) a->Id) << 8);
-                               for (i = 0; i < appl->MaxBuffer; i++)
-                               {
-                                       if ((appl->DataNCCI[i] == ncci_code)
-                                           && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
-                                       {
-                                               appl->DataNCCI[i] = 0;
-                                       }
-                               }
-                       }
-               }
-       }
-       else
-       {
-               for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
-               {
-                       if (a->ncci_plci[ncci] == plci->Id)
-                       {
-                               if (!plci->appl)
-                               {
-                                       ncci_mapping_bug++;
-                                       dbug(1, dprintf("NCCI mapping no appl %ld %08lx",
-                                                       ncci_mapping_bug, Id));
-                               }
-                               else
-                               {
-                                       appl = plci->appl;
-                                       ncci_code = ncci | (((word) a->Id) << 8);
-                                       for (i = 0; i < appl->MaxBuffer; i++)
-                                       {
-                                               if ((appl->DataNCCI[i] == ncci_code)
-                                                   && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
-                                               {
-                                                       appl->DataNCCI[i] = 0;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-
-static void cleanup_ncci_data(PLCI *plci, word ncci)
-{
-       NCCI *ncci_ptr;
-
-       if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id))
-       {
-               ncci_ptr = &(plci->adapter->ncci[ncci]);
-               if (plci->appl)
-               {
-                       while (ncci_ptr->data_pending != 0)
-                       {
-                               if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr))
-                                       TransmitBufferFree(plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P);
-                               (ncci_ptr->data_out)++;
-                               if (ncci_ptr->data_out == MAX_DATA_B3)
-                                       ncci_ptr->data_out = 0;
-                               (ncci_ptr->data_pending)--;
-                       }
-               }
-               ncci_ptr->data_out = 0;
-               ncci_ptr->data_pending = 0;
-               ncci_ptr->data_ack_out = 0;
-               ncci_ptr->data_ack_pending = 0;
-       }
-}
-
-
-static void ncci_remove(PLCI *plci, word ncci, byte preserve_ncci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       dword Id;
-       word i;
-
-       a = plci->adapter;
-       Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
-       if (!preserve_ncci)
-               ncci_free_receive_buffers(plci, ncci);
-       if (ncci)
-       {
-               if (a->ncci_plci[ncci] != plci->Id)
-               {
-                       ncci_mapping_bug++;
-                       dbug(1, dprintf("NCCI mapping doesn't exist %ld %08lx %02x",
-                                       ncci_mapping_bug, Id, preserve_ncci));
-               }
-               else
-               {
-                       cleanup_ncci_data(plci, ncci);
-                       dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
-                                       ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
-                       a->ch_ncci[a->ncci_ch[ncci]] = 0;
-                       if (!preserve_ncci)
-                       {
-                               a->ncci_ch[ncci] = 0;
-                               a->ncci_plci[ncci] = 0;
-                               a->ncci_state[ncci] = IDLE;
-                               i = plci->ncci_ring_list;
-                               while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci))
-                                       i = a->ncci_next[i];
-                               if ((i != 0) && (a->ncci_next[i] == ncci))
-                               {
-                                       if (i == ncci)
-                                               plci->ncci_ring_list = 0;
-                                       else if (plci->ncci_ring_list == ncci)
-                                               plci->ncci_ring_list = i;
-                                       a->ncci_next[i] = a->ncci_next[ncci];
-                               }
-                               a->ncci_next[ncci] = 0;
-                       }
-               }
-       }
-       else
-       {
-               for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
-               {
-                       if (a->ncci_plci[ncci] == plci->Id)
-                       {
-                               cleanup_ncci_data(plci, ncci);
-                               dbug(1, dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
-                                               ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
-                               a->ch_ncci[a->ncci_ch[ncci]] = 0;
-                               if (!preserve_ncci)
-                               {
-                                       a->ncci_ch[ncci] = 0;
-                                       a->ncci_plci[ncci] = 0;
-                                       a->ncci_state[ncci] = IDLE;
-                                       a->ncci_next[ncci] = 0;
-                               }
-                       }
-               }
-               if (!preserve_ncci)
-                       plci->ncci_ring_list = 0;
-       }
-}
-
-
-/*------------------------------------------------------------------*/
-/* PLCI remove function                                             */
-/*------------------------------------------------------------------*/
-
-static void plci_free_msg_in_queue(PLCI *plci)
-{
-       word i;
-
-       if (plci->appl)
-       {
-               i = plci->msg_in_read_pos;
-               while (i != plci->msg_in_write_pos)
-               {
-                       if (i == plci->msg_in_wrap_pos)
-                               i = 0;
-                       if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R)
-                       {
-
-                               TransmitBufferFree(plci->appl,
-                                                  (byte *)(long)(((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data));
-
-                       }
-
-                       i += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.length +
-                             MSG_IN_OVERHEAD + 3) & 0xfffc;
-
-               }
-       }
-       plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
-       plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
-       plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
-}
-
-
-static void plci_remove(PLCI *plci)
-{
-
-       if (!plci) {
-               dbug(1, dprintf("plci_remove(no plci)"));
-               return;
-       }
-       init_internal_command_queue(plci);
-       dbug(1, dprintf("plci_remove(%x,tel=%x)", plci->Id, plci->tel));
-       if (plci_remove_check(plci))
-       {
-               return;
-       }
-       if (plci->Sig.Id == 0xff)
-       {
-               dbug(1, dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id));
-               if (plci->NL.Id && !plci->nl_remove_id)
-               {
-                       nl_req_ncci(plci, REMOVE, 0);
-                       send_req(plci);
-               }
-       }
-       else
-       {
-               if (!plci->sig_remove_id
-                   && (plci->Sig.Id
-                       || (plci->req_in != plci->req_out)
-                       || (plci->nl_req || plci->sig_req)))
-               {
-                       sig_req(plci, HANGUP, 0);
-                       send_req(plci);
-               }
-       }
-       ncci_remove(plci, 0, false);
-       plci_free_msg_in_queue(plci);
-
-       plci->channels = 0;
-       plci->appl = NULL;
-       if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT))
-               plci->State = OUTG_DIS_PENDING;
-}
-
-/*------------------------------------------------------------------*/
-/* translation function for each message                            */
-/*------------------------------------------------------------------*/
-
-static byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                       PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ch;
-       word i;
-       word Info;
-       byte LinkLayer;
-       API_PARSE *ai;
-       API_PARSE *bp;
-       API_PARSE ai_parms[5];
-       word channel = 0;
-       dword ch_mask;
-       byte m;
-       static byte esc_chi[35] = {0x02, 0x18, 0x01};
-       static byte lli[2] = {0x01, 0x00};
-       byte noCh = 0;
-       word dir = 0;
-       byte *p_chi = "";
-
-       for (i = 0; i < 5; i++) ai_parms[i].length = 0;
-
-       dbug(1, dprintf("connect_req(%d)", parms->length));
-       Info = _WRONG_IDENTIFIER;
-       if (a)
-       {
-               if (a->adapter_disabled)
-               {
-                       dbug(1, dprintf("adapter disabled"));
-                       Id = ((word)1 << 8) | a->Id;
-                       sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
-                       sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR);
-                       return false;
-               }
-               Info = _OUT_OF_PLCI;
-               if ((i = get_plci(a)))
-               {
-                       Info = 0;
-                       plci = &a->plci[i - 1];
-                       plci->appl = appl;
-                       plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-                       /* check 'external controller' bit for codec support */
-                       if (Id & EXT_CONTROLLER)
-                       {
-                               if (AdvCodecSupport(a, plci, appl, 0))
-                               {
-                                       plci->Id = 0;
-                                       sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
-                                       return 2;
-                               }
-                       }
-                       ai = &parms[9];
-                       bp = &parms[5];
-                       ch = 0;
-                       if (bp->length)LinkLayer = bp->info[3];
-                       else LinkLayer = 0;
-                       if (ai->length)
-                       {
-                               ch = 0xffff;
-                               if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
-                               {
-                                       ch = 0;
-                                       if (ai_parms[0].length)
-                                       {
-                                               ch = GET_WORD(ai_parms[0].info + 1);
-                                               if (ch > 4) ch = 0; /* safety -> ignore ChannelID */
-                                               if (ch == 4) /* explizit CHI in message */
-                                               {
-                                                       /* check length of B-CH struct */
-                                                       if ((ai_parms[0].info)[3] >= 1)
-                                                       {
-                                                               if ((ai_parms[0].info)[4] == CHI)
-                                                               {
-                                                                       p_chi = &((ai_parms[0].info)[5]);
-                                                               }
-                                                               else
-                                                               {
-                                                                       p_chi = &((ai_parms[0].info)[3]);
-                                                               }
-                                                               if (p_chi[0] > 35) /* check length of channel ID */
-                                                               {
-                                                                       Info = _WRONG_MESSAGE_FORMAT;
-                                                               }
-                                                       }
-                                                       else Info = _WRONG_MESSAGE_FORMAT;
-                                               }
-
-                                               if (ch == 3 && ai_parms[0].length >= 7 && ai_parms[0].length <= 36)
-                                               {
-                                                       dir = GET_WORD(ai_parms[0].info + 3);
-                                                       ch_mask = 0;
-                                                       m = 0x3f;
-                                                       for (i = 0; i + 5 <= ai_parms[0].length; i++)
-                                                       {
-                                                               if (ai_parms[0].info[i + 5] != 0)
-                                                               {
-                                                                       if ((ai_parms[0].info[i + 5] | m) != 0xff)
-                                                                               Info = _WRONG_MESSAGE_FORMAT;
-                                                                       else
-                                                                       {
-                                                                               if (ch_mask == 0)
-                                                                                       channel = i;
-                                                                               ch_mask |= 1L << i;
-                                                                       }
-                                                               }
-                                                               m = 0;
-                                                       }
-                                                       if (ch_mask == 0)
-                                                               Info = _WRONG_MESSAGE_FORMAT;
-                                                       if (!Info)
-                                                       {
-                                                               if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel))))
-                                                               {
-                                                                       esc_chi[0] = (byte)(ai_parms[0].length - 2);
-                                                                       for (i = 0; i + 5 <= ai_parms[0].length; i++)
-                                                                               esc_chi[i + 3] = ai_parms[0].info[i + 5];
-                                                               }
-                                                               else
-                                                                       esc_chi[0] = 2;
-                                                               esc_chi[2] = (byte)channel;
-                                                               plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */
-                                                               add_p(plci, LLI, lli);
-                                                               add_p(plci, ESC, esc_chi);
-                                                               plci->State = LOCAL_CONNECT;
-                                                               if (!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;     /* dir 0=DTE, 1=DCE */
-                                                       }
-                                               }
-                                       }
-                               }
-                               else  Info = _WRONG_MESSAGE_FORMAT;
-                       }
-
-                       dbug(1, dprintf("ch=%x,dir=%x,p_ch=%d", ch, dir, channel));
-                       plci->command = _CONNECT_R;
-                       plci->number = Number;
-                       /* x.31 or D-ch free SAPI in LinkLayer? */
-                       if (ch == 1 && LinkLayer != 3 && LinkLayer != 12) noCh = true;
-                       if ((ch == 0 || ch == 2 || noCh || ch == 3 || ch == 4) && !Info)
-                       {
-                               /* B-channel used for B3 connections (ch==0), or no B channel    */
-                               /* is used (ch==2) or perm. connection (3) is used  do a CALL    */
-                               if (noCh) Info = add_b1(plci, &parms[5], 2, 0);    /* no resource    */
-                               else     Info = add_b1(plci, &parms[5], ch, 0);
-                               add_s(plci, OAD, &parms[2]);
-                               add_s(plci, OSA, &parms[4]);
-                               add_s(plci, BC, &parms[6]);
-                               add_s(plci, LLC, &parms[7]);
-                               add_s(plci, HLC, &parms[8]);
-                               if (a->Info_Mask[appl->Id - 1] & 0x200)
-                               {
-                                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                                       add_p(plci, LLI, "\x01\x01");
-                               }
-                               if (GET_WORD(parms[0].info) < 29) {
-                                       add_p(plci, BC, cip_bc[GET_WORD(parms[0].info)][a->u_law]);
-                                       add_p(plci, HLC, cip_hlc[GET_WORD(parms[0].info)]);
-                               }
-                               add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                               sig_req(plci, ASSIGN, DSIG_ID);
-                       }
-                       else if (ch == 1) {
-
-                               /* D-Channel used for B3 connections */
-                               plci->Sig.Id = 0xff;
-                               Info = 0;
-                       }
-
-                       if (!Info && ch != 2 && !noCh) {
-                               Info = add_b23(plci, &parms[5]);
-                               if (!Info) {
-                                       if (!(plci->tel && !plci->adv_nl))nl_req_ncci(plci, ASSIGN, 0);
-                               }
-                       }
-
-                       if (!Info)
-                       {
-                               if (ch == 0 || ch == 2 || ch == 3 || noCh || ch == 4)
-                               {
-                                       if (plci->spoofed_msg == SPOOFING_REQUIRED)
-                                       {
-                                               api_save_msg(parms, "wsssssssss", &plci->saved_msg);
-                                               plci->spoofed_msg = CALL_REQ;
-                                               plci->internal_command = BLOCK_PLCI;
-                                               plci->command = 0;
-                                               dbug(1, dprintf("Spoof"));
-                                               send_req(plci);
-                                               return false;
-                                       }
-                                       if (ch == 4)add_p(plci, CHI, p_chi);
-                                       add_s(plci, CPN, &parms[1]);
-                                       add_s(plci, DSA, &parms[3]);
-                                       if (noCh) add_p(plci, ESC, "\x02\x18\xfd");  /* D-channel, no B-L3 */
-                                       add_ai(plci, &parms[9]);
-                                       if (!dir)sig_req(plci, CALL_REQ, 0);
-                                       else
-                                       {
-                                               plci->command = PERM_LIST_REQ;
-                                               plci->appl = appl;
-                                               sig_req(plci, LISTEN_REQ, 0);
-                                               send_req(plci);
-                                               return false;
-                                       }
-                               }
-                               send_req(plci);
-                               return false;
-                       }
-                       plci->Id = 0;
-               }
-       }
-       sendf(appl,
-             _CONNECT_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-       return 2;
-}
-
-static byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                       PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word i, Info;
-       word Reject;
-       static byte cau_t[] = {0, 0, 0x90, 0x91, 0xac, 0x9d, 0x86, 0xd8, 0x9b};
-       static byte esc_t[] = {0x03, 0x08, 0x00, 0x00};
-       API_PARSE *ai;
-       API_PARSE ai_parms[5];
-       word ch = 0;
-
-       if (!plci) {
-               dbug(1, dprintf("connect_res(no plci)"));
-               return 0;  /* no plci, no send */
-       }
-
-       dbug(1, dprintf("connect_res(State=0x%x)", plci->State));
-       for (i = 0; i < 5; i++) ai_parms[i].length = 0;
-       ai = &parms[5];
-       dbug(1, dprintf("ai->length=%d", ai->length));
-
-       if (ai->length)
-       {
-               if (!api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
-               {
-                       dbug(1, dprintf("ai_parms[0].length=%d/0x%x", ai_parms[0].length, GET_WORD(ai_parms[0].info + 1)));
-                       ch = 0;
-                       if (ai_parms[0].length)
-                       {
-                               ch = GET_WORD(ai_parms[0].info + 1);
-                               dbug(1, dprintf("BCH-I=0x%x", ch));
-                       }
-               }
-       }
-
-       if (plci->State == INC_CON_CONNECTED_ALERT)
-       {
-               dbug(1, dprintf("Connected Alert Call_Res"));
-               if (a->Info_Mask[appl->Id - 1] & 0x200)
-               {
-                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                       add_p(plci, LLI, "\x01\x01");
-               }
-               add_s(plci, CONN_NR, &parms[2]);
-               add_s(plci, LLC, &parms[4]);
-               add_ai(plci, &parms[5]);
-               plci->State = INC_CON_ACCEPT;
-               sig_req(plci, CALL_RES, 0);
-               return 1;
-       }
-       else if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT) {
-               __clear_bit(appl->Id - 1, plci->c_ind_mask_table);
-               dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table));
-               Reject = GET_WORD(parms[0].info);
-               dbug(1, dprintf("Reject=0x%x", Reject));
-               if (Reject)
-               {
-                       if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL))
-                       {
-                               if ((Reject & 0xff00) == 0x3400)
-                               {
-                                       esc_t[2] = ((byte)(Reject & 0x00ff)) | 0x80;
-                                       add_p(plci, ESC, esc_t);
-                                       add_ai(plci, &parms[5]);
-                                       sig_req(plci, REJECT, 0);
-                               }
-                               else if (Reject == 1 || Reject >= 9)
-                               {
-                                       add_ai(plci, &parms[5]);
-                                       sig_req(plci, HANGUP, 0);
-                               }
-                               else
-                               {
-                                       esc_t[2] = cau_t[(Reject&0x000f)];
-                                       add_p(plci, ESC, esc_t);
-                                       add_ai(plci, &parms[5]);
-                                       sig_req(plci, REJECT, 0);
-                               }
-                               plci->appl = appl;
-                       }
-                       else
-                       {
-                               sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
-                       }
-               }
-               else {
-                       plci->appl = appl;
-                       if (Id & EXT_CONTROLLER) {
-                               if (AdvCodecSupport(a, plci, appl, 0)) {
-                                       dbug(1, dprintf("connect_res(error from AdvCodecSupport)"));
-                                       sig_req(plci, HANGUP, 0);
-                                       return 1;
-                               }
-                               if (plci->tel == ADV_VOICE && a->AdvCodecPLCI)
-                               {
-                                       Info = add_b23(plci, &parms[1]);
-                                       if (Info)
-                                       {
-                                               dbug(1, dprintf("connect_res(error from add_b23)"));
-                                               sig_req(plci, HANGUP, 0);
-                                               return 1;
-                                       }
-                                       if (plci->adv_nl)
-                                       {
-                                               nl_req_ncci(plci, ASSIGN, 0);
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               plci->tel = 0;
-                               if (ch != 2)
-                               {
-                                       Info = add_b23(plci, &parms[1]);
-                                       if (Info)
-                                       {
-                                               dbug(1, dprintf("connect_res(error from add_b23 2)"));
-                                               sig_req(plci, HANGUP, 0);
-                                               return 1;
-                                       }
-                               }
-                               nl_req_ncci(plci, ASSIGN, 0);
-                       }
-
-                       if (plci->spoofed_msg == SPOOFING_REQUIRED)
-                       {
-                               api_save_msg(parms, "wsssss", &plci->saved_msg);
-                               plci->spoofed_msg = CALL_RES;
-                               plci->internal_command = BLOCK_PLCI;
-                               plci->command = 0;
-                               dbug(1, dprintf("Spoof"));
-                       }
-                       else
-                       {
-                               add_b1(plci, &parms[1], ch, plci->B1_facilities);
-                               if (a->Info_Mask[appl->Id - 1] & 0x200)
-                               {
-                                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                                       add_p(plci, LLI, "\x01\x01");
-                               }
-                               add_s(plci, CONN_NR, &parms[2]);
-                               add_s(plci, LLC, &parms[4]);
-                               add_ai(plci, &parms[5]);
-                               plci->State = INC_CON_ACCEPT;
-                               sig_req(plci, CALL_RES, 0);
-                       }
-
-                       for_each_set_bit(i, plci->c_ind_mask_table, max_appl)
-                               sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
-               }
-       }
-       return 1;
-}
-
-static byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                         PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       dbug(1, dprintf("connect_a_res"));
-       return false;
-}
-
-static byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                          PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word Info;
-       word i;
-
-       dbug(1, dprintf("disconnect_req"));
-
-       Info = _WRONG_IDENTIFIER;
-
-       if (plci)
-       {
-               if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT)
-               {
-                       __clear_bit(appl->Id - 1, plci->c_ind_mask_table);
-                       plci->appl = appl;
-                       for_each_set_bit(i, plci->c_ind_mask_table, max_appl)
-                               sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
-                       plci->State = OUTG_DIS_PENDING;
-               }
-               if (plci->Sig.Id && plci->appl)
-               {
-                       Info = 0;
-                       if (plci->Sig.Id != 0xff)
-                       {
-                               if (plci->State != INC_DIS_PENDING)
-                               {
-                                       add_ai(plci, &msg[0]);
-                                       sig_req(plci, HANGUP, 0);
-                                       plci->State = OUTG_DIS_PENDING;
-                                       return 1;
-                               }
-                       }
-                       else
-                       {
-                               if (plci->NL.Id && !plci->nl_remove_id)
-                               {
-                                       mixer_remove(plci);
-                                       nl_req_ncci(plci, REMOVE, 0);
-                                       sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0);
-                                       sendf(appl, _DISCONNECT_I, Id, 0, "w", 0);
-                                       plci->State = INC_DIS_PENDING;
-                               }
-                               return 1;
-                       }
-               }
-       }
-
-       if (!appl)  return false;
-       sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", Info);
-       return false;
-}
-
-static byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                          PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       dbug(1, dprintf("disconnect_res"));
-       if (plci)
-       {
-               /* clear ind mask bit, just in case of collsion of          */
-               /* DISCONNECT_IND and CONNECT_RES                           */
-               __clear_bit(appl->Id - 1, plci->c_ind_mask_table);
-               ncci_free_receive_buffers(plci, 0);
-               if (plci_remove_check(plci))
-               {
-                       return 0;
-               }
-               if (plci->State == INC_DIS_PENDING
-                   || plci->State == SUSPENDING) {
-                       if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) {
-                               if (plci->State != SUSPENDING) plci->State = IDLE;
-                               dbug(1, dprintf("chs=%d", plci->channels));
-                               if (!plci->channels) {
-                                       plci_remove(plci);
-                               }
-                       }
-               }
-       }
-       return 0;
-}
-
-static byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                      PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word Info;
-       byte i;
-
-       dbug(1, dprintf("listen_req(Appl=0x%x)", appl->Id));
-
-       Info = _WRONG_IDENTIFIER;
-       if (a) {
-               Info = 0;
-               a->Info_Mask[appl->Id - 1] = GET_DWORD(parms[0].info);
-               a->CIP_Mask[appl->Id - 1] = GET_DWORD(parms[1].info);
-               dbug(1, dprintf("CIP_MASK=0x%lx", GET_DWORD(parms[1].info)));
-               if (a->Info_Mask[appl->Id - 1] & 0x200) { /* early B3 connect provides */
-                       a->Info_Mask[appl->Id - 1] |=  0x10;   /* call progression infos    */
-               }
-
-               /* check if external controller listen and switch listen on or off*/
-               if (Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)) {
-                       if (a->profile.Global_Options & ON_BOARD_CODEC) {
-                               dummy_plci.State = IDLE;
-                               a->codec_listen[appl->Id - 1] = &dummy_plci;
-                               a->TelOAD[0] = (byte)(parms[3].length);
-                               for (i = 1; parms[3].length >= i && i < 22; i++) {
-                                       a->TelOAD[i] = parms[3].info[i];
-                               }
-                               a->TelOAD[i] = 0;
-                               a->TelOSA[0] = (byte)(parms[4].length);
-                               for (i = 1; parms[4].length >= i && i < 22; i++) {
-                                       a->TelOSA[i] = parms[4].info[i];
-                               }
-                               a->TelOSA[i] = 0;
-                       }
-                       else Info = 0x2002; /* wrong controller, codec not supported */
-               }
-               else{               /* clear listen */
-                       a->codec_listen[appl->Id - 1] = (PLCI *)0;
-               }
-       }
-       sendf(appl,
-             _LISTEN_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-
-       if (a) listen_check(a);
-       return false;
-}
-
-static byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                    PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word i;
-       API_PARSE *ai;
-       PLCI *rc_plci = NULL;
-       API_PARSE ai_parms[5];
-       word Info = 0;
-
-       dbug(1, dprintf("info_req"));
-       for (i = 0; i < 5; i++) ai_parms[i].length = 0;
-
-       ai = &msg[1];
-
-       if (ai->length)
-       {
-               if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
-               {
-                       dbug(1, dprintf("AddInfo wrong"));
-                       Info = _WRONG_MESSAGE_FORMAT;
-               }
-       }
-       if (!a) Info = _WRONG_STATE;
-
-       if (!Info && plci)
-       {                /* no fac, with CPN, or KEY */
-               rc_plci = plci;
-               if (!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length))
-               {
-                       /* overlap sending option */
-                       dbug(1, dprintf("OvlSnd"));
-                       add_s(plci, CPN, &msg[0]);
-                       add_s(plci, KEY, &ai_parms[1]);
-                       sig_req(plci, INFO_REQ, 0);
-                       send_req(plci);
-                       return false;
-               }
-
-               if (plci->State && ai_parms[2].length)
-               {
-                       /* User_Info option */
-                       dbug(1, dprintf("UUI"));
-                       add_s(plci, UUI, &ai_parms[2]);
-                       sig_req(plci, USER_DATA, 0);
-               }
-               else if (plci->State && ai_parms[3].length)
-               {
-                       /* Facility option */
-                       dbug(1, dprintf("FAC"));
-                       add_s(plci, CPN, &msg[0]);
-                       add_ai(plci, &msg[1]);
-                       sig_req(plci, FACILITY_REQ, 0);
-               }
-               else
-               {
-                       Info = _WRONG_STATE;
-               }
-       }
-       else if ((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info)
-       {
-               /* NCR_Facility option -> send UUI and Keypad too */
-               dbug(1, dprintf("NCR_FAC"));
-               if ((i = get_plci(a)))
-               {
-                       rc_plci = &a->plci[i - 1];
-                       appl->NullCREnable = true;
-                       rc_plci->internal_command = C_NCR_FAC_REQ;
-                       rc_plci->appl = appl;
-                       add_p(rc_plci, CAI, "\x01\x80");
-                       add_p(rc_plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                       sig_req(rc_plci, ASSIGN, DSIG_ID);
-                       send_req(rc_plci);
-               }
-               else
-               {
-                       Info = _OUT_OF_PLCI;
-               }
-
-               if (!Info)
-               {
-                       add_s(rc_plci, CPN, &msg[0]);
-                       add_ai(rc_plci, &msg[1]);
-                       sig_req(rc_plci, NCR_FACILITY, 0);
-                       send_req(rc_plci);
-                       return false;
-                       /* for application controlled supplementary services    */
-               }
-       }
-
-       if (!rc_plci)
-       {
-               Info = _WRONG_MESSAGE_FORMAT;
-       }
-
-       if (!Info)
-       {
-               send_req(rc_plci);
-       }
-       else
-       {  /* appl is not assigned to a PLCI or error condition */
-               dbug(1, dprintf("localInfoCon"));
-               sendf(appl,
-                     _INFO_R | CONFIRM,
-                     Id,
-                     Number,
-                     "w", Info);
-       }
-       return false;
-}
-
-static byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                    PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       dbug(1, dprintf("info_res"));
-       return false;
-}
-
-static byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                     PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word Info;
-       byte ret;
-
-       dbug(1, dprintf("alert_req"));
-
-       Info = _WRONG_IDENTIFIER;
-       ret = false;
-       if (plci) {
-               Info = _ALERT_IGNORED;
-               if (plci->State != INC_CON_ALERT) {
-                       Info = _WRONG_STATE;
-                       if (plci->State == INC_CON_PENDING) {
-                               Info = 0;
-                               plci->State = INC_CON_ALERT;
-                               add_ai(plci, &msg[0]);
-                               sig_req(plci, CALL_ALERT, 0);
-                               ret = 1;
-                       }
-               }
-       }
-       sendf(appl,
-             _ALERT_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-       return ret;
-}
-
-static byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                        PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word Info = 0;
-       word i    = 0;
-
-       word selector;
-       word SSreq;
-       long relatedPLCIvalue;
-       DIVA_CAPI_ADAPTER *relatedadapter;
-       byte *SSparms  = "";
-       byte RCparms[]  = "\x05\x00\x00\x02\x00\x00";
-       byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
-       API_PARSE *parms;
-       API_PARSE ss_parms[11];
-       PLCI *rplci;
-       byte cai[15];
-       dword d;
-       API_PARSE dummy;
-
-       dbug(1, dprintf("facility_req"));
-       for (i = 0; i < 9; i++) ss_parms[i].length = 0;
-
-       parms = &msg[1];
-
-       if (!a)
-       {
-               dbug(1, dprintf("wrong Ctrl"));
-               Info = _WRONG_IDENTIFIER;
-       }
-
-       selector = GET_WORD(msg[0].info);
-
-       if (!Info)
-       {
-               switch (selector)
-               {
-               case SELECTOR_HANDSET:
-                       Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT);
-                       break;
-
-               case SELECTOR_SU_SERV:
-                       if (!msg[1].length)
-                       {
-                               Info = _WRONG_MESSAGE_FORMAT;
-                               break;
-                       }
-                       SSreq = GET_WORD(&(msg[1].info[1]));
-                       PUT_WORD(&RCparms[1], SSreq);
-                       SSparms = RCparms;
-                       switch (SSreq)
-                       {
-                       case S_GET_SUPPORTED_SERVICES:
-                               if ((i = get_plci(a)))
-                               {
-                                       rplci = &a->plci[i - 1];
-                                       rplci->appl = appl;
-                                       add_p(rplci, CAI, "\x01\x80");
-                                       add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                       sig_req(rplci, ASSIGN, DSIG_ID);
-                                       send_req(rplci);
-                               }
-                               else
-                               {
-                                       PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
-                                       SSparms = (byte *)SSstruct;
-                                       break;
-                               }
-                               rplci->internal_command = GETSERV_REQ_PEND;
-                               rplci->number = Number;
-                               rplci->appl = appl;
-                               sig_req(rplci, S_SUPPORTED, 0);
-                               send_req(rplci);
-                               return false;
-                               break;
-
-                       case S_LISTEN:
-                               if (parms->length == 7)
-                               {
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               a->Notification_Mask[appl->Id - 1] = GET_DWORD(ss_parms[2].info);
-                               if (a->Notification_Mask[appl->Id - 1] & SMASK_MWI) /* MWI active? */
-                               {
-                                       if ((i = get_plci(a)))
-                                       {
-                                               rplci = &a->plci[i - 1];
-                                               rplci->appl = appl;
-                                               add_p(rplci, CAI, "\x01\x80");
-                                               add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               sig_req(rplci, ASSIGN, DSIG_ID);
-                                               send_req(rplci);
-                                       }
-                                       else
-                                       {
-                                               break;
-                                       }
-                                       rplci->internal_command = GET_MWI_STATE;
-                                       rplci->number = Number;
-                                       sig_req(rplci, MWI_POLL, 0);
-                                       send_req(rplci);
-                               }
-                               break;
-
-                       case S_HOLD:
-                               api_parse(&parms->info[1], (word)parms->length, "ws", ss_parms);
-                               if (plci && plci->State && plci->SuppState == IDLE)
-                               {
-                                       plci->SuppState = HOLD_REQUEST;
-                                       plci->command = C_HOLD_REQ;
-                                       add_s(plci, CAI, &ss_parms[1]);
-                                       sig_req(plci, CALL_HOLD, 0);
-                                       send_req(plci);
-                                       return false;
-                               }
-                               else Info = 0x3010;                    /* wrong state           */
-                               break;
-                       case S_RETRIEVE:
-                               if (plci && plci->State && plci->SuppState == CALL_HELD)
-                               {
-                                       if (Id & EXT_CONTROLLER)
-                                       {
-                                               if (AdvCodecSupport(a, plci, appl, 0))
-                                               {
-                                                       Info = 0x3010;                    /* wrong state           */
-                                                       break;
-                                               }
-                                       }
-                                       else plci->tel = 0;
-
-                                       plci->SuppState = RETRIEVE_REQUEST;
-                                       plci->command = C_RETRIEVE_REQ;
-                                       if (plci->spoofed_msg == SPOOFING_REQUIRED)
-                                       {
-                                               plci->spoofed_msg = CALL_RETRIEVE;
-                                               plci->internal_command = BLOCK_PLCI;
-                                               plci->command = 0;
-                                               dbug(1, dprintf("Spoof"));
-                                               return false;
-                                       }
-                                       else
-                                       {
-                                               sig_req(plci, CALL_RETRIEVE, 0);
-                                               send_req(plci);
-                                               return false;
-                                       }
-                               }
-                               else Info = 0x3010;                    /* wrong state           */
-                               break;
-                       case S_SUSPEND:
-                               if (parms->length)
-                               {
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms))
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                               }
-                               if (plci && plci->State)
-                               {
-                                       add_s(plci, CAI, &ss_parms[2]);
-                                       plci->command = SUSPEND_REQ;
-                                       sig_req(plci, SUSPEND, 0);
-                                       plci->State = SUSPENDING;
-                                       send_req(plci);
-                               }
-                               else Info = 0x3010;                    /* wrong state           */
-                               break;
-
-                       case S_RESUME:
-                               if (!(i = get_plci(a)))
-                               {
-                                       Info = _OUT_OF_PLCI;
-                                       break;
-                               }
-                               rplci = &a->plci[i - 1];
-                               rplci->appl = appl;
-                               rplci->number = Number;
-                               rplci->tel = 0;
-                               rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-                               /* check 'external controller' bit for codec support */
-                               if (Id & EXT_CONTROLLER)
-                               {
-                                       if (AdvCodecSupport(a, rplci, appl, 0))
-                                       {
-                                               rplci->Id = 0;
-                                               Info = 0x300A;
-                                               break;
-                                       }
-                               }
-                               if (parms->length)
-                               {
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbs", ss_parms))
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               rplci->Id = 0;
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                               }
-                               dummy.length = 0;
-                               dummy.info = "\x00";
-                               add_b1(rplci, &dummy, 0, 0);
-                               if (a->Info_Mask[appl->Id - 1] & 0x200)
-                               {
-                                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                                       add_p(rplci, LLI, "\x01\x01");
-                               }
-                               add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                               sig_req(rplci, ASSIGN, DSIG_ID);
-                               send_req(rplci);
-                               add_s(rplci, CAI, &ss_parms[2]);
-                               rplci->command = RESUME_REQ;
-                               sig_req(rplci, RESUME, 0);
-                               rplci->State = RESUMING;
-                               send_req(rplci);
-                               break;
-
-                       case S_CONF_BEGIN: /* Request */
-                       case S_CONF_DROP:
-                       case S_CONF_ISOLATE:
-                       case S_CONF_REATTACH:
-                               if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
-                               {
-                                       dbug(1, dprintf("format wrong"));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (plci && plci->State && ((plci->SuppState == IDLE) || (plci->SuppState == CALL_HELD)))
-                               {
-                                       d = GET_DWORD(ss_parms[2].info);
-                                       if (d >= 0x80)
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                                       plci->ptyState = (byte)SSreq;
-                                       plci->command = 0;
-                                       cai[0] = 2;
-                                       switch (SSreq)
-                                       {
-                                       case S_CONF_BEGIN:
-                                               cai[1] = CONF_BEGIN;
-                                               plci->internal_command = CONF_BEGIN_REQ_PEND;
-                                               break;
-                                       case S_CONF_DROP:
-                                               cai[1] = CONF_DROP;
-                                               plci->internal_command = CONF_DROP_REQ_PEND;
-                                               break;
-                                       case S_CONF_ISOLATE:
-                                               cai[1] = CONF_ISOLATE;
-                                               plci->internal_command = CONF_ISOLATE_REQ_PEND;
-                                               break;
-                                       case S_CONF_REATTACH:
-                                               cai[1] = CONF_REATTACH;
-                                               plci->internal_command = CONF_REATTACH_REQ_PEND;
-                                               break;
-                                       }
-                                       cai[2] = (byte)d; /* Conference Size resp. PartyId */
-                                       add_p(plci, CAI, cai);
-                                       sig_req(plci, S_SERVICE, 0);
-                                       send_req(plci);
-                                       return false;
-                               }
-                               else Info = 0x3010;                    /* wrong state           */
-                               break;
-
-                       case S_ECT:
-                       case S_3PTY_BEGIN:
-                       case S_3PTY_END:
-                       case S_CONF_ADD:
-                               if (parms->length == 7)
-                               {
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                               }
-                               else if (parms->length == 8) /* workaround for the T-View-S */
-                               {
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbdb", ss_parms))
-                                       {
-                                               dbug(1, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (!msg[1].length)
-                               {
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (!plci)
-                               {
-                                       Info = _WRONG_IDENTIFIER;
-                                       break;
-                               }
-                               relatedPLCIvalue = GET_DWORD(ss_parms[2].info);
-                               relatedPLCIvalue &= 0x0000FFFF;
-                               dbug(1, dprintf("PTY/ECT/addCONF,relPLCI=%lx", relatedPLCIvalue));
-                               /* controller starts with 0 up to (max_adapter - 1) */
-                               if (((relatedPLCIvalue & 0x7f) == 0)
-                                   || (MapController((byte)(relatedPLCIvalue & 0x7f)) == 0)
-                                   || (MapController((byte)(relatedPLCIvalue & 0x7f)) > max_adapter))
-                               {
-                                       if (SSreq == S_3PTY_END)
-                                       {
-                                               dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI"));
-                                               rplci = plci;
-                                       }
-                                       else
-                                       {
-                                               Info = 0x3010;                    /* wrong state           */
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       relatedadapter = &adapter[MapController((byte)(relatedPLCIvalue & 0x7f)) - 1];
-                                       relatedPLCIvalue >>= 8;
-                                       /* find PLCI PTR*/
-                                       for (i = 0, rplci = NULL; i < relatedadapter->max_plci; i++)
-                                       {
-                                               if (relatedadapter->plci[i].Id == (byte)relatedPLCIvalue)
-                                               {
-                                                       rplci = &relatedadapter->plci[i];
-                                               }
-                                       }
-                                       if (!rplci || !relatedPLCIvalue)
-                                       {
-                                               if (SSreq == S_3PTY_END)
-                                               {
-                                                       dbug(1, dprintf("use 2nd PLCI=PLCI"));
-                                                       rplci = plci;
-                                               }
-                                               else
-                                               {
-                                                       Info = 0x3010;                    /* wrong state           */
-                                                       break;
-                                               }
-                                       }
-                               }
-/*
-  dbug(1, dprintf("rplci:%x", rplci));
-  dbug(1, dprintf("plci:%x", plci));
-  dbug(1, dprintf("rplci->ptyState:%x", rplci->ptyState));
-  dbug(1, dprintf("plci->ptyState:%x", plci->ptyState));
-  dbug(1, dprintf("SSreq:%x", SSreq));
-  dbug(1, dprintf("rplci->internal_command:%x", rplci->internal_command));
-  dbug(1, dprintf("rplci->appl:%x", rplci->appl));
-  dbug(1, dprintf("rplci->Id:%x", rplci->Id));
-*/
-                               /* send PTY/ECT req, cannot check all states because of US stuff */
-                               if (!rplci->internal_command && rplci->appl)
-                               {
-                                       plci->command = 0;
-                                       rplci->relatedPTYPLCI = plci;
-                                       plci->relatedPTYPLCI = rplci;
-                                       rplci->ptyState = (byte)SSreq;
-                                       if (SSreq == S_ECT)
-                                       {
-                                               rplci->internal_command = ECT_REQ_PEND;
-                                               cai[1] = ECT_EXECUTE;
-
-                                               rplci->vswitchstate = 0;
-                                               rplci->vsprot = 0;
-                                               rplci->vsprotdialect = 0;
-                                               plci->vswitchstate = 0;
-                                               plci->vsprot = 0;
-                                               plci->vsprotdialect = 0;
-
-                                       }
-                                       else if (SSreq == S_CONF_ADD)
-                                       {
-                                               rplci->internal_command = CONF_ADD_REQ_PEND;
-                                               cai[1] = CONF_ADD;
-                                       }
-                                       else
-                                       {
-                                               rplci->internal_command = PTY_REQ_PEND;
-                                               cai[1] = (byte)(SSreq - 3);
-                                       }
-                                       rplci->number = Number;
-                                       if (plci != rplci) /* explicit invocation */
-                                       {
-                                               cai[0] = 2;
-                                               cai[2] = plci->Sig.Id;
-                                               dbug(1, dprintf("explicit invocation"));
-                                       }
-                                       else
-                                       {
-                                               dbug(1, dprintf("implicit invocation"));
-                                               cai[0] = 1;
-                                       }
-                                       add_p(rplci, CAI, cai);
-                                       sig_req(rplci, S_SERVICE, 0);
-                                       send_req(rplci);
-                                       return false;
-                               }
-                               else
-                               {
-                                       dbug(0, dprintf("Wrong line"));
-                                       Info = 0x3010;                    /* wrong state           */
-                                       break;
-                               }
-                               break;
-
-                       case S_CALL_DEFLECTION:
-                               if (api_parse(&parms->info[1], (word)parms->length, "wbwss", ss_parms))
-                               {
-                                       dbug(1, dprintf("format wrong"));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (!plci)
-                               {
-                                       Info = _WRONG_IDENTIFIER;
-                                       break;
-                               }
-                               /* reuse unused screening indicator */
-                               ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0]));
-                               plci->command = 0;
-                               plci->internal_command = CD_REQ_PEND;
-                               appl->CDEnable = true;
-                               cai[0] = 1;
-                               cai[1] = CALL_DEFLECTION;
-                               add_p(plci, CAI, cai);
-                               add_p(plci, CPN, ss_parms[3].info);
-                               sig_req(plci, S_SERVICE, 0);
-                               send_req(plci);
-                               return false;
-                               break;
-
-                       case S_CALL_FORWARDING_START:
-                               if (api_parse(&parms->info[1], (word)parms->length, "wbdwwsss", ss_parms))
-                               {
-                                       dbug(1, dprintf("format wrong"));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-
-                               if ((i = get_plci(a)))
-                               {
-                                       rplci = &a->plci[i - 1];
-                                       rplci->appl = appl;
-                                       add_p(rplci, CAI, "\x01\x80");
-                                       add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                       sig_req(rplci, ASSIGN, DSIG_ID);
-                                       send_req(rplci);
-                               }
-                               else
-                               {
-                                       Info = _OUT_OF_PLCI;
-                                       break;
-                               }
-
-                               /* reuse unused screening indicator */
-                               rplci->internal_command = CF_START_PEND;
-                               rplci->appl = appl;
-                               rplci->number = Number;
-                               appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
-                               cai[0] = 2;
-                               cai[1] = 0x70 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
-                               cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
-                               add_p(rplci, CAI, cai);
-                               add_p(rplci, OAD, ss_parms[5].info);
-                               add_p(rplci, CPN, ss_parms[6].info);
-                               sig_req(rplci, S_SERVICE, 0);
-                               send_req(rplci);
-                               return false;
-                               break;
-
-                       case S_INTERROGATE_DIVERSION:
-                       case S_INTERROGATE_NUMBERS:
-                       case S_CALL_FORWARDING_STOP:
-                       case S_CCBS_REQUEST:
-                       case S_CCBS_DEACTIVATE:
-                       case S_CCBS_INTERROGATE:
-                               switch (SSreq)
-                               {
-                               case S_INTERROGATE_NUMBERS:
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbd", ss_parms))
-                                       {
-                                               dbug(0, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                       }
-                                       break;
-                               case S_CCBS_REQUEST:
-                               case S_CCBS_DEACTIVATE:
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbdw", ss_parms))
-                                       {
-                                               dbug(0, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                       }
-                                       break;
-                               case S_CCBS_INTERROGATE:
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbdws", ss_parms))
-                                       {
-                                               dbug(0, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                       }
-                                       break;
-                               default:
-                                       if (api_parse(&parms->info[1], (word)parms->length, "wbdwws", ss_parms))
-                                       {
-                                               dbug(0, dprintf("format wrong"));
-                                               Info = _WRONG_MESSAGE_FORMAT;
-                                               break;
-                                       }
-                                       break;
-                               }
-
-                               if (Info) break;
-                               if ((i = get_plci(a)))
-                               {
-                                       rplci = &a->plci[i - 1];
-                                       switch (SSreq)
-                                       {
-                                       case S_INTERROGATE_DIVERSION: /* use cai with S_SERVICE below */
-                                               cai[1] = 0x60 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
-                                               rplci->internal_command = INTERR_DIVERSION_REQ_PEND; /* move to rplci if assigned */
-                                               break;
-                                       case S_INTERROGATE_NUMBERS: /* use cai with S_SERVICE below */
-                                               cai[1] = DIVERSION_INTERROGATE_NUM; /* Function */
-                                               rplci->internal_command = INTERR_NUMBERS_REQ_PEND; /* move to rplci if assigned */
-                                               break;
-                                       case S_CALL_FORWARDING_STOP:
-                                               rplci->internal_command = CF_STOP_PEND;
-                                               cai[1] = 0x80 | (byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
-                                               break;
-                                       case S_CCBS_REQUEST:
-                                               cai[1] = CCBS_REQUEST;
-                                               rplci->internal_command = CCBS_REQUEST_REQ_PEND;
-                                               break;
-                                       case S_CCBS_DEACTIVATE:
-                                               cai[1] = CCBS_DEACTIVATE;
-                                               rplci->internal_command = CCBS_DEACTIVATE_REQ_PEND;
-                                               break;
-                                       case S_CCBS_INTERROGATE:
-                                               cai[1] = CCBS_INTERROGATE;
-                                               rplci->internal_command = CCBS_INTERROGATE_REQ_PEND;
-                                               break;
-                                       default:
-                                               cai[1] = 0;
-                                               break;
-                                       }
-                                       rplci->appl = appl;
-                                       rplci->number = Number;
-                                       add_p(rplci, CAI, "\x01\x80");
-                                       add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                       sig_req(rplci, ASSIGN, DSIG_ID);
-                                       send_req(rplci);
-                               }
-                               else
-                               {
-                                       Info = _OUT_OF_PLCI;
-                                       break;
-                               }
-
-                               appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
-                               switch (SSreq)
-                               {
-                               case S_INTERROGATE_NUMBERS:
-                                       cai[0] = 1;
-                                       add_p(rplci, CAI, cai);
-                                       break;
-                               case S_CCBS_REQUEST:
-                               case S_CCBS_DEACTIVATE:
-                                       cai[0] = 3;
-                                       PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0])));
-                                       add_p(rplci, CAI, cai);
-                                       break;
-                               case S_CCBS_INTERROGATE:
-                                       cai[0] = 3;
-                                       PUT_WORD(&cai[2], GET_WORD(&(ss_parms[3].info[0])));
-                                       add_p(rplci, CAI, cai);
-                                       add_p(rplci, OAD, ss_parms[4].info);
-                                       break;
-                               default:
-                                       cai[0] = 2;
-                                       cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
-                                       add_p(rplci, CAI, cai);
-                                       add_p(rplci, OAD, ss_parms[5].info);
-                                       break;
-                               }
-
-                               sig_req(rplci, S_SERVICE, 0);
-                               send_req(rplci);
-                               return false;
-                               break;
-
-                       case S_MWI_ACTIVATE:
-                               if (api_parse(&parms->info[1], (word)parms->length, "wbwdwwwssss", ss_parms))
-                               {
-                                       dbug(1, dprintf("format wrong"));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (!plci)
-                               {
-                                       if ((i = get_plci(a)))
-                                       {
-                                               rplci = &a->plci[i - 1];
-                                               rplci->appl = appl;
-                                               rplci->cr_enquiry = true;
-                                               add_p(rplci, CAI, "\x01\x80");
-                                               add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               sig_req(rplci, ASSIGN, DSIG_ID);
-                                               send_req(rplci);
-                                       }
-                                       else
-                                       {
-                                               Info = _OUT_OF_PLCI;
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       rplci = plci;
-                                       rplci->cr_enquiry = false;
-                               }
-
-                               rplci->command = 0;
-                               rplci->internal_command = MWI_ACTIVATE_REQ_PEND;
-                               rplci->appl = appl;
-                               rplci->number = Number;
-
-                               cai[0] = 13;
-                               cai[1] = ACTIVATION_MWI; /* Function */
-                               PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
-                               PUT_DWORD(&cai[4], GET_DWORD(&(ss_parms[3].info[0]))); /* Number of Messages */
-                               PUT_WORD(&cai[8], GET_WORD(&(ss_parms[4].info[0]))); /* Message Status */
-                               PUT_WORD(&cai[10], GET_WORD(&(ss_parms[5].info[0]))); /* Message Reference */
-                               PUT_WORD(&cai[12], GET_WORD(&(ss_parms[6].info[0]))); /* Invocation Mode */
-                               add_p(rplci, CAI, cai);
-                               add_p(rplci, CPN, ss_parms[7].info); /* Receiving User Number */
-                               add_p(rplci, OAD, ss_parms[8].info); /* Controlling User Number */
-                               add_p(rplci, OSA, ss_parms[9].info); /* Controlling User Provided Number */
-                               add_p(rplci, UID, ss_parms[10].info); /* Time */
-                               sig_req(rplci, S_SERVICE, 0);
-                               send_req(rplci);
-                               return false;
-
-                       case S_MWI_DEACTIVATE:
-                               if (api_parse(&parms->info[1], (word)parms->length, "wbwwss", ss_parms))
-                               {
-                                       dbug(1, dprintf("format wrong"));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (!plci)
-                               {
-                                       if ((i = get_plci(a)))
-                                       {
-                                               rplci = &a->plci[i - 1];
-                                               rplci->appl = appl;
-                                               rplci->cr_enquiry = true;
-                                               add_p(rplci, CAI, "\x01\x80");
-                                               add_p(rplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               sig_req(rplci, ASSIGN, DSIG_ID);
-                                               send_req(rplci);
-                                       }
-                                       else
-                                       {
-                                               Info = _OUT_OF_PLCI;
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       rplci = plci;
-                                       rplci->cr_enquiry = false;
-                               }
-
-                               rplci->command = 0;
-                               rplci->internal_command = MWI_DEACTIVATE_REQ_PEND;
-                               rplci->appl = appl;
-                               rplci->number = Number;
-
-                               cai[0] = 5;
-                               cai[1] = DEACTIVATION_MWI; /* Function */
-                               PUT_WORD(&cai[2], GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
-                               PUT_WORD(&cai[4], GET_WORD(&(ss_parms[3].info[0]))); /* Invocation Mode */
-                               add_p(rplci, CAI, cai);
-                               add_p(rplci, CPN, ss_parms[4].info); /* Receiving User Number */
-                               add_p(rplci, OAD, ss_parms[5].info); /* Controlling User Number */
-                               sig_req(rplci, S_SERVICE, 0);
-                               send_req(rplci);
-                               return false;
-
-                       default:
-                               Info = 0x300E;  /* not supported */
-                               break;
-                       }
-                       break; /* case SELECTOR_SU_SERV: end */
-
-
-               case SELECTOR_DTMF:
-                       return (dtmf_request(Id, Number, a, plci, appl, msg));
-
-
-
-               case SELECTOR_LINE_INTERCONNECT:
-                       return (mixer_request(Id, Number, a, plci, appl, msg));
-
-
-
-               case PRIV_SELECTOR_ECHO_CANCELLER:
-                       appl->appl_flags |= APPL_FLAG_PRIV_EC_SPEC;
-                       return (ec_request(Id, Number, a, plci, appl, msg));
-
-               case SELECTOR_ECHO_CANCELLER:
-                       appl->appl_flags &= ~APPL_FLAG_PRIV_EC_SPEC;
-                       return (ec_request(Id, Number, a, plci, appl, msg));
-
-
-               case SELECTOR_V42BIS:
-               default:
-                       Info = _FACILITY_NOT_SUPPORTED;
-                       break;
-               } /* end of switch (selector) */
-       }
-
-       dbug(1, dprintf("SendFacRc"));
-       sendf(appl,
-             _FACILITY_R | CONFIRM,
-             Id,
-             Number,
-             "wws", Info, selector, SSparms);
-       return false;
-}
-
-static byte facility_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                        PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       dbug(1, dprintf("facility_res"));
-       return false;
-}
-
-static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                          PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word Info = 0;
-       byte req;
-       byte len;
-       word w;
-       word fax_control_bits, fax_feature_bits, fax_info_change;
-       API_PARSE *ncpi;
-       byte pvc[2];
-
-       API_PARSE fax_parms[9];
-       word i;
-
-
-       dbug(1, dprintf("connect_b3_req"));
-       if (plci)
-       {
-               if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING)
-                   || (plci->State == INC_DIS_PENDING) || (plci->SuppState != IDLE))
-               {
-                       Info = _WRONG_STATE;
-               }
-               else
-               {
-                       /* local reply if assign unsuccessful
-                          or B3 protocol allows only one layer 3 connection
-                          and already connected
-                          or B2 protocol not any LAPD
-                          and connect_b3_req contradicts originate/answer direction */
-                       if (!plci->NL.Id
-                           || (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
-                               && ((plci->channels != 0)
-                                   || (((plci->B2_prot != B2_SDLC) && (plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL))
-                                       && ((plci->call_dir & CALL_DIR_ANSWER) && !(plci->call_dir & CALL_DIR_FORCE_OUTG_NL))))))
-                       {
-                               dbug(1, dprintf("B3 already connected=%d or no NL.Id=0x%x, dir=%d sstate=0x%x",
-                                               plci->channels, plci->NL.Id, plci->call_dir, plci->SuppState));
-                               Info = _WRONG_STATE;
-                               sendf(appl,
-                                     _CONNECT_B3_R | CONFIRM,
-                                     Id,
-                                     Number,
-                                     "w", Info);
-                               return false;
-                       }
-                       plci->requested_options_conn = 0;
-
-                       req = N_CONNECT;
-                       ncpi = &parms[0];
-                       if (plci->B3_prot == 2 || plci->B3_prot == 3)
-                       {
-                               if (ncpi->length > 2)
-                               {
-                                       /* check for PVC */
-                                       if (ncpi->info[2] || ncpi->info[3])
-                                       {
-                                               pvc[0] = ncpi->info[3];
-                                               pvc[1] = ncpi->info[2];
-                                               add_d(plci, 2, pvc);
-                                               req = N_RESET;
-                                       }
-                                       else
-                                       {
-                                               if (ncpi->info[1] & 1) req = N_CONNECT | N_D_BIT;
-                                               add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
-                                       }
-                               }
-                       }
-                       else if (plci->B3_prot == 5)
-                       {
-                               if (plci->NL.Id && !plci->nl_remove_id)
-                               {
-                                       fax_control_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low);
-                                       fax_feature_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low);
-                                       if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS)
-                                           || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS))
-                                       {
-                                               len = offsetof(T30_INFO, universal_6);
-                                               fax_info_change = false;
-                                               if (ncpi->length >= 4)
-                                               {
-                                                       w = GET_WORD(&ncpi->info[3]);
-                                                       if ((w & 0x0001) != ((word)(((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & 0x0001)))
-                                                       {
-                                                               ((T30_INFO *)(plci->fax_connect_info_buffer))->resolution =
-                                                                       (byte)((((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & ~T30_RESOLUTION_R8_0770_OR_200) |
-                                                                              ((w & 0x0001) ? T30_RESOLUTION_R8_0770_OR_200 : 0));
-                                                               fax_info_change = true;
-                                                       }
-                                                       fax_control_bits &= ~(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
-                                                       if (w & 0x0002)  /* Fax-polling request */
-                                                               fax_control_bits |= T30_CONTROL_BIT_REQUEST_POLLING;
-                                                       if ((w & 0x0004) /* Request to send / poll another document */
-                                                           && (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS))
-                                                       {
-                                                               fax_control_bits |= T30_CONTROL_BIT_MORE_DOCUMENTS;
-                                                       }
-                                                       if (ncpi->length >= 6)
-                                                       {
-                                                               w = GET_WORD(&ncpi->info[5]);
-                                                               if (((byte) w) != ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format)
-                                                               {
-                                                                       ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format = (byte) w;
-                                                                       fax_info_change = true;
-                                                               }
-
-                                                               if ((a->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
-                                                                   && (GET_WORD(&ncpi->info[5]) & 0x8000)) /* Private SEP/SUB/PWD enable */
-                                                               {
-                                                                       plci->requested_options_conn |= (1L << PRIVATE_FAX_SUB_SEP_PWD);
-                                                               }
-                                                               if ((a->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
-                                                                   && (GET_WORD(&ncpi->info[5]) & 0x4000)) /* Private non-standard facilities enable */
-                                                               {
-                                                                       plci->requested_options_conn |= (1L << PRIVATE_FAX_NONSTANDARD);
-                                                               }
-                                                               fax_control_bits &= ~(T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_SEL_POLLING |
-                                                                                     T30_CONTROL_BIT_ACCEPT_PASSWORD);
-                                                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
-                                                                   & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
-                                                               {
-                                                                       if (api_parse(&ncpi->info[1], ncpi->length, "wwwwsss", fax_parms))
-                                                                               Info = _WRONG_MESSAGE_FORMAT;
-                                                                       else
-                                                                       {
-                                                                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
-                                                                                   & (1L << PRIVATE_FAX_SUB_SEP_PWD))
-                                                                               {
-                                                                                       fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
-                                                                                       if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
-                                                                                               fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
-                                                                               }
-                                                                               w = fax_parms[4].length;
-                                                                               if (w > 20)
-                                                                                       w = 20;
-                                                                               ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = (byte) w;
-                                                                               for (i = 0; i < w; i++)
-                                                                                       ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1 + i];
-                                                                               ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
-                                                                               len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
-                                                                               w = fax_parms[5].length;
-                                                                               if (w > 20)
-                                                                                       w = 20;
-                                                                               plci->fax_connect_info_buffer[len++] = (byte) w;
-                                                                               for (i = 0; i < w; i++)
-                                                                                       plci->fax_connect_info_buffer[len++] = fax_parms[5].info[1 + i];
-                                                                               w = fax_parms[6].length;
-                                                                               if (w > 20)
-                                                                                       w = 20;
-                                                                               plci->fax_connect_info_buffer[len++] = (byte) w;
-                                                                               for (i = 0; i < w; i++)
-                                                                                       plci->fax_connect_info_buffer[len++] = fax_parms[6].info[1 + i];
-                                                                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id - 1])
-                                                                                   & (1L << PRIVATE_FAX_NONSTANDARD))
-                                                                               {
-                                                                                       if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
-                                                                                       {
-                                                                                               dbug(1, dprintf("non-standard facilities info missing or wrong format"));
-                                                                                               plci->fax_connect_info_buffer[len++] = 0;
-                                                                                       }
-                                                                                       else
-                                                                                       {
-                                                                                               if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
-                                                                                                       plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
-                                                                                               plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
-                                                                                               for (i = 0; i < fax_parms[7].length; i++)
-                                                                                                       plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
-                                                                                       }
-                                                                               }
-                                                                       }
-                                                               }
-                                                               else
-                                                               {
-                                                                       len = offsetof(T30_INFO, universal_6);
-                                                               }
-                                                               fax_info_change = true;
-
-                                                       }
-                                                       if (fax_control_bits != GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low))
-                                                       {
-                                                               PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low, fax_control_bits);
-                                                               fax_info_change = true;
-                                                       }
-                                               }
-                                               if (Info == GOOD)
-                                               {
-                                                       plci->fax_connect_info_length = len;
-                                                       if (fax_info_change)
-                                                       {
-                                                               if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
-                                                               {
-                                                                       start_internal_command(Id, plci, fax_connect_info_command);
-                                                                       return false;
-                                                               }
-                                                               else
-                                                               {
-                                                                       start_internal_command(Id, plci, fax_adjust_b23_command);
-                                                                       return false;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                                       else  Info = _WRONG_STATE;
-                               }
-                               else  Info = _WRONG_STATE;
-                       }
-
-                       else if (plci->B3_prot == B3_RTP)
-                       {
-                               plci->internal_req_buffer[0] = ncpi->length + 1;
-                               plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
-                               for (w = 0; w < ncpi->length; w++)
-                                       plci->internal_req_buffer[2 + w] = ncpi->info[1 + w];
-                               start_internal_command(Id, plci, rtp_connect_b3_req_command);
-                               return false;
-                       }
-
-                       if (!Info)
-                       {
-                               nl_req_ncci(plci, req, 0);
-                               return 1;
-                       }
-               }
-       }
-       else Info = _WRONG_IDENTIFIER;
-
-       sendf(appl,
-             _CONNECT_B3_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-       return false;
-}
-
-static byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                          PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ncci;
-       API_PARSE *ncpi;
-       byte req;
-
-       word w;
-
-
-       API_PARSE fax_parms[9];
-       word i;
-       byte len;
-
-
-       dbug(1, dprintf("connect_b3_res"));
-
-       ncci = (word)(Id >> 16);
-       if (plci && ncci) {
-               if (a->ncci_state[ncci] == INC_CON_PENDING) {
-                       if (GET_WORD(&parms[0].info[0]) != 0)
-                       {
-                               a->ncci_state[ncci] = OUTG_REJ_PENDING;
-                               channel_request_xon(plci, a->ncci_ch[ncci]);
-                               channel_xmit_xon(plci);
-                               cleanup_ncci_data(plci, ncci);
-                               nl_req_ncci(plci, N_DISC, (byte)ncci);
-                               return 1;
-                       }
-                       a->ncci_state[ncci] = INC_ACT_PENDING;
-
-                       req = N_CONNECT_ACK;
-                       ncpi = &parms[1];
-                       if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
-                       {
-
-                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
-                                   & (1L << PRIVATE_FAX_NONSTANDARD))
-                               {
-                                       if (((plci->B3_prot == 4) || (plci->B3_prot == 5))
-                                           && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
-                                           && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
-                                       {
-                                               len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
-                                               if (plci->fax_connect_info_length < len)
-                                               {
-                                                       ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
-                                                       ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
-                                               }
-                                               if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
-                                               {
-                                                       dbug(1, dprintf("non-standard facilities info missing or wrong format"));
-                                               }
-                                               else
-                                               {
-                                                       if (plci->fax_connect_info_length <= len)
-                                                               plci->fax_connect_info_buffer[len] = 0;
-                                                       len += 1 + plci->fax_connect_info_buffer[len];
-                                                       if (plci->fax_connect_info_length <= len)
-                                                               plci->fax_connect_info_buffer[len] = 0;
-                                                       len += 1 + plci->fax_connect_info_buffer[len];
-                                                       if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
-                                                               plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
-                                                       plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
-                                                       for (i = 0; i < fax_parms[7].length; i++)
-                                                               plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
-                                               }
-                                               plci->fax_connect_info_length = len;
-                                               ((T30_INFO *)(plci->fax_connect_info_buffer))->code = 0;
-                                               start_internal_command(Id, plci, fax_connect_ack_command);
-                                               return false;
-                                       }
-                               }
-
-                               nl_req_ncci(plci, req, (byte)ncci);
-                               if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                                   && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                               {
-                                       if (plci->B3_prot == 4)
-                                               sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                                       else
-                                               sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-                                       plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                               }
-                       }
-
-                       else if (plci->B3_prot == B3_RTP)
-                       {
-                               plci->internal_req_buffer[0] = ncpi->length + 1;
-                               plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
-                               for (w = 0; w < ncpi->length; w++)
-                                       plci->internal_req_buffer[2 + w] = ncpi->info[1+w];
-                               start_internal_command(Id, plci, rtp_connect_b3_res_command);
-                               return false;
-                       }
-
-                       else
-                       {
-                               if (ncpi->length > 2) {
-                                       if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT;
-                                       add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
-                               }
-                               nl_req_ncci(plci, req, (byte)ncci);
-                               sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                               if (plci->adjust_b_restore)
-                               {
-                                       plci->adjust_b_restore = false;
-                                       start_internal_command(Id, plci, adjust_b_restore);
-                               }
-                       }
-                       return 1;
-               }
-       }
-       return false;
-}
-
-static byte connect_b3_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                            PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ncci;
-
-       ncci = (word)(Id >> 16);
-       dbug(1, dprintf("connect_b3_a_res(ncci=0x%x)", ncci));
-
-       if (plci && ncci && (plci->State != IDLE) && (plci->State != INC_DIS_PENDING)
-           && (plci->State != OUTG_DIS_PENDING))
-       {
-               if (a->ncci_state[ncci] == INC_ACT_PENDING) {
-                       a->ncci_state[ncci] = CONNECTED;
-                       if (plci->State != INC_CON_CONNECTED_ALERT) plci->State = CONNECTED;
-                       channel_request_xon(plci, a->ncci_ch[ncci]);
-                       channel_xmit_xon(plci);
-               }
-       }
-       return false;
-}
-
-static byte disconnect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                             PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word Info;
-       word ncci;
-       API_PARSE *ncpi;
-
-       dbug(1, dprintf("disconnect_b3_req"));
-
-       Info = _WRONG_IDENTIFIER;
-       ncci = (word)(Id >> 16);
-       if (plci && ncci)
-       {
-               Info = _WRONG_STATE;
-               if ((a->ncci_state[ncci] == CONNECTED)
-                   || (a->ncci_state[ncci] == OUTG_CON_PENDING)
-                   || (a->ncci_state[ncci] == INC_CON_PENDING)
-                   || (a->ncci_state[ncci] == INC_ACT_PENDING))
-               {
-                       a->ncci_state[ncci] = OUTG_DIS_PENDING;
-                       channel_request_xon(plci, a->ncci_ch[ncci]);
-                       channel_xmit_xon(plci);
-
-                       if (a->ncci[ncci].data_pending
-                           && ((plci->B3_prot == B3_TRANSPARENT)
-                               || (plci->B3_prot == B3_T30)
-                               || (plci->B3_prot == B3_T30_WITH_EXTENSIONS)))
-                       {
-                               plci->send_disc = (byte)ncci;
-                               plci->command = 0;
-                               return false;
-                       }
-                       else
-                       {
-                               cleanup_ncci_data(plci, ncci);
-
-                               if (plci->B3_prot == 2 || plci->B3_prot == 3)
-                               {
-                                       ncpi = &parms[0];
-                                       if (ncpi->length > 3)
-                                       {
-                                               add_d(plci, (word)(ncpi->length - 3), (byte *)&(ncpi->info[4]));
-                                       }
-                               }
-                               nl_req_ncci(plci, N_DISC, (byte)ncci);
-                       }
-                       return 1;
-               }
-       }
-       sendf(appl,
-             _DISCONNECT_B3_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-       return false;
-}
-
-static byte disconnect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                             PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ncci;
-       word i;
-
-       ncci = (word)(Id >> 16);
-       dbug(1, dprintf("disconnect_b3_res(ncci=0x%x", ncci));
-       if (plci && ncci) {
-               plci->requested_options_conn = 0;
-               plci->fax_connect_info_length = 0;
-               plci->ncpi_state = 0x00;
-               if (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
-                   && ((plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL)))
-               {
-                       plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
-               }
-               for (i = 0; i < MAX_CHANNELS_PER_PLCI && plci->inc_dis_ncci_table[i] != (byte)ncci; i++);
-               if (i < MAX_CHANNELS_PER_PLCI) {
-                       if (plci->channels)plci->channels--;
-                       for (; i < MAX_CHANNELS_PER_PLCI - 1; i++) plci->inc_dis_ncci_table[i] = plci->inc_dis_ncci_table[i + 1];
-                       plci->inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI - 1] = 0;
-
-                       ncci_free_receive_buffers(plci, ncci);
-
-                       if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) {
-                               if (plci->State == SUSPENDING) {
-                                       sendf(plci->appl,
-                                             _FACILITY_I,
-                                             Id & 0xffffL,
-                                             0,
-                                             "ws", (word)3, "\x03\x04\x00\x00");
-                                       sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
-                               }
-                               plci_remove(plci);
-                               plci->State = IDLE;
-                       }
-               }
-               else
-               {
-                       if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                           && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
-                           && (a->ncci_state[ncci] == INC_DIS_PENDING))
-                       {
-                               ncci_free_receive_buffers(plci, ncci);
-
-                               nl_req_ncci(plci, N_EDATA, (byte)ncci);
-
-                               plci->adapter->ncci_state[ncci] = IDLE;
-                               start_internal_command(Id, plci, fax_disconnect_command);
-                               return 1;
-                       }
-               }
-       }
-       return false;
-}
-
-static byte data_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                       PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       NCCI *ncci_ptr;
-       DATA_B3_DESC *data;
-       word Info;
-       word ncci;
-       word i;
-
-       dbug(1, dprintf("data_b3_req"));
-
-       Info = _WRONG_IDENTIFIER;
-       ncci = (word)(Id >> 16);
-       dbug(1, dprintf("ncci=0x%x, plci=0x%x", ncci, plci));
-
-       if (plci && ncci)
-       {
-               Info = _WRONG_STATE;
-               if ((a->ncci_state[ncci] == CONNECTED)
-                   || (a->ncci_state[ncci] == INC_ACT_PENDING))
-               {
-                       /* queue data */
-                       ncci_ptr = &(a->ncci[ncci]);
-                       i = ncci_ptr->data_out + ncci_ptr->data_pending;
-                       if (i >= MAX_DATA_B3)
-                               i -= MAX_DATA_B3;
-                       data = &(ncci_ptr->DBuffer[i]);
-                       data->Number = Number;
-                       if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
-                           && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
-                       {
-
-                               data->P = (byte *)(long)(*((dword *)(parms[0].info)));
-
-                       }
-                       else
-                               data->P = TransmitBufferSet(appl, *(dword *)parms[0].info);
-                       data->Length = GET_WORD(parms[1].info);
-                       data->Handle = GET_WORD(parms[2].info);
-                       data->Flags = GET_WORD(parms[3].info);
-                       (ncci_ptr->data_pending)++;
-
-                       /* check for delivery confirmation */
-                       if (data->Flags & 0x0004)
-                       {
-                               i = ncci_ptr->data_ack_out + ncci_ptr->data_ack_pending;
-                               if (i >= MAX_DATA_ACK)
-                                       i -= MAX_DATA_ACK;
-                               ncci_ptr->DataAck[i].Number = data->Number;
-                               ncci_ptr->DataAck[i].Handle = data->Handle;
-                               (ncci_ptr->data_ack_pending)++;
-                       }
-
-                       send_data(plci);
-                       return false;
-               }
-       }
-       if (appl)
-       {
-               if (plci)
-               {
-                       if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
-                           && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
-                       {
-
-                               TransmitBufferFree(appl, (byte *)(long)(*((dword *)(parms[0].info))));
-
-                       }
-               }
-               sendf(appl,
-                     _DATA_B3_R | CONFIRM,
-                     Id,
-                     Number,
-                     "ww", GET_WORD(parms[2].info), Info);
-       }
-       return false;
-}
-
-static byte data_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                       PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word n;
-       word ncci;
-       word NCCIcode;
-
-       dbug(1, dprintf("data_b3_res"));
-
-       ncci = (word)(Id >> 16);
-       if (plci && ncci) {
-               n = GET_WORD(parms[0].info);
-               dbug(1, dprintf("free(%d)", n));
-               NCCIcode = ncci | (((word) a->Id) << 8);
-               if (n < appl->MaxBuffer &&
-                   appl->DataNCCI[n] == NCCIcode &&
-                   (byte)(appl->DataFlags[n] >> 8) == plci->Id) {
-                       dbug(1, dprintf("found"));
-                       appl->DataNCCI[n] = 0;
-
-                       if (channel_can_xon(plci, a->ncci_ch[ncci])) {
-                               channel_request_xon(plci, a->ncci_ch[ncci]);
-                       }
-                       channel_xmit_xon(plci);
-
-                       if (appl->DataFlags[n] & 4) {
-                               nl_req_ncci(plci, N_DATA_ACK, (byte)ncci);
-                               return 1;
-                       }
-               }
-       }
-       return false;
-}
-
-static byte reset_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                        PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word Info;
-       word ncci;
-
-       dbug(1, dprintf("reset_b3_req"));
-
-       Info = _WRONG_IDENTIFIER;
-       ncci = (word)(Id >> 16);
-       if (plci && ncci)
-       {
-               Info = _WRONG_STATE;
-               switch (plci->B3_prot)
-               {
-               case B3_ISO8208:
-               case B3_X25_DCE:
-                       if (a->ncci_state[ncci] == CONNECTED)
-                       {
-                               nl_req_ncci(plci, N_RESET, (byte)ncci);
-                               send_req(plci);
-                               Info = GOOD;
-                       }
-                       break;
-               case B3_TRANSPARENT:
-                       if (a->ncci_state[ncci] == CONNECTED)
-                       {
-                               start_internal_command(Id, plci, reset_b3_command);
-                               Info = GOOD;
-                       }
-                       break;
-               }
-       }
-       /* reset_b3 must result in a reset_b3_con & reset_b3_Ind */
-       sendf(appl,
-             _RESET_B3_R | CONFIRM,
-             Id,
-             Number,
-             "w", Info);
-       return false;
-}
-
-static byte reset_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                        PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ncci;
-
-       dbug(1, dprintf("reset_b3_res"));
-
-       ncci = (word)(Id >> 16);
-       if (plci && ncci) {
-               switch (plci->B3_prot)
-               {
-               case B3_ISO8208:
-               case B3_X25_DCE:
-                       if (a->ncci_state[ncci] == INC_RES_PENDING)
-                       {
-                               a->ncci_state[ncci] = CONNECTED;
-                               nl_req_ncci(plci, N_RESET_ACK, (byte)ncci);
-                               return true;
-                       }
-                       break;
-               }
-       }
-       return false;
-}
-
-static byte connect_b3_t90_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                                PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word ncci;
-       API_PARSE *ncpi;
-       byte req;
-
-       dbug(1, dprintf("connect_b3_t90_a_res"));
-
-       ncci = (word)(Id >> 16);
-       if (plci && ncci) {
-               if (a->ncci_state[ncci] == INC_ACT_PENDING) {
-                       a->ncci_state[ncci] = CONNECTED;
-               }
-               else if (a->ncci_state[ncci] == INC_CON_PENDING) {
-                       a->ncci_state[ncci] = CONNECTED;
-
-                       req = N_CONNECT_ACK;
-
-                       /* parms[0]==0 for CAPI original message definition! */
-                       if (parms[0].info) {
-                               ncpi = &parms[1];
-                               if (ncpi->length > 2) {
-                                       if (ncpi->info[1] & 1) req = N_CONNECT_ACK | N_D_BIT;
-                                       add_d(plci, (word)(ncpi->length - 3), &ncpi->info[4]);
-                               }
-                       }
-                       nl_req_ncci(plci, req, (byte)ncci);
-                       return 1;
-               }
-       }
-       return false;
-}
-
-
-static byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                        PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word Info = 0;
-       word i;
-       byte tel;
-       API_PARSE bp_parms[7];
-
-       if (!plci || !msg)
-       {
-               Info = _WRONG_IDENTIFIER;
-       }
-       else
-       {
-               dbug(1, dprintf("select_b_req[%d],PLCI=0x%x,Tel=0x%x,NL=0x%x,appl=0x%x,sstate=0x%x",
-                               msg->length, plci->Id, plci->tel, plci->NL.Id, plci->appl, plci->SuppState));
-               dbug(1, dprintf("PlciState=0x%x", plci->State));
-               for (i = 0; i < 7; i++) bp_parms[i].length = 0;
-
-               /* check if no channel is open, no B3 connected only */
-               if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) || (plci->State == INC_DIS_PENDING)
-                   || (plci->SuppState != IDLE) || plci->channels || plci->nl_remove_id)
-               {
-                       Info = _WRONG_STATE;
-               }
-               /* check message format and fill bp_parms pointer */
-               else if (msg->length && api_parse(&msg->info[1], (word)msg->length, "wwwsss", bp_parms))
-               {
-                       Info = _WRONG_MESSAGE_FORMAT;
-               }
-               else
-               {
-                       if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT)) /* send alert tone inband to the network, */
-                       {                                                                  /* e.g. Qsig or RBS or Cornet-N or xess PRI */
-                               if (Id & EXT_CONTROLLER)
-                               {
-                                       sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", 0x2002); /* wrong controller */
-                                       return 0;
-                               }
-                               plci->State = INC_CON_CONNECTED_ALERT;
-                               plci->appl = appl;
-                               __clear_bit(appl->Id - 1, plci->c_ind_mask_table);
-                               dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table));
-                               /* disconnect the other appls its quasi a connect */
-                               for_each_set_bit(i, plci->c_ind_mask_table, max_appl)
-                                       sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
-                       }
-
-                       api_save_msg(msg, "s", &plci->saved_msg);
-                       tel = plci->tel;
-                       if (Id & EXT_CONTROLLER)
-                       {
-                               if (tel) /* external controller in use by this PLCI */
-                               {
-                                       if (a->AdvSignalAppl && a->AdvSignalAppl != appl)
-                                       {
-                                               dbug(1, dprintf("Ext_Ctrl in use 1"));
-                                               Info = _WRONG_STATE;
-                                       }
-                               }
-                               else  /* external controller NOT in use by this PLCI ? */
-                               {
-                                       if (a->AdvSignalPLCI)
-                                       {
-                                               dbug(1, dprintf("Ext_Ctrl in use 2"));
-                                               Info = _WRONG_STATE;
-                                       }
-                                       else /* activate the codec */
-                                       {
-                                               dbug(1, dprintf("Ext_Ctrl start"));
-                                               if (AdvCodecSupport(a, plci, appl, 0))
-                                               {
-                                                       dbug(1, dprintf("Error in codec procedures"));
-                                                       Info = _WRONG_STATE;
-                                               }
-                                               else if (plci->spoofed_msg == SPOOFING_REQUIRED) /* wait until codec is active */
-                                               {
-                                                       plci->spoofed_msg = AWAITING_SELECT_B;
-                                                       plci->internal_command = BLOCK_PLCI; /* lock other commands */
-                                                       plci->command = 0;
-                                                       dbug(1, dprintf("continue if codec loaded"));
-                                                       return false;
-                                               }
-                                       }
-                               }
-                       }
-                       else /* external controller bit is OFF */
-                       {
-                               if (tel) /* external controller in use, need to switch off */
-                               {
-                                       if (a->AdvSignalAppl == appl)
-                                       {
-                                               CodecIdCheck(a, plci);
-                                               plci->tel = 0;
-                                               plci->adv_nl = 0;
-                                               dbug(1, dprintf("Ext_Ctrl disable"));
-                                       }
-                                       else
-                                       {
-                                               dbug(1, dprintf("Ext_Ctrl not requested"));
-                                       }
-                               }
-                       }
-                       if (!Info)
-                       {
-                               if (plci->call_dir & CALL_DIR_OUT)
-                                       plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-                               else if (plci->call_dir & CALL_DIR_IN)
-                                       plci->call_dir = CALL_DIR_IN | CALL_DIR_ANSWER;
-                               start_internal_command(Id, plci, select_b_command);
-                               return false;
-                       }
-               }
-       }
-       sendf(appl, _SELECT_B_REQ | CONFIRM, Id, Number, "w", Info);
-       return false;
-}
-
-static byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                            PLCI *plci, APPL *appl, API_PARSE *parms)
-{
-       word command;
-       word i;
-       word ncci;
-       API_PARSE *m;
-       API_PARSE m_parms[5];
-       word codec;
-       byte req;
-       byte ch;
-       byte dir;
-       static byte chi[2] = {0x01, 0x00};
-       static byte lli[2] = {0x01, 0x00};
-       static byte codec_cai[2] = {0x01, 0x01};
-       static byte null_msg = {0};
-       static API_PARSE null_parms = { 0, &null_msg };
-       PLCI *v_plci;
-       word Info = 0;
-
-       dbug(1, dprintf("manufacturer_req"));
-       for (i = 0; i < 5; i++) m_parms[i].length = 0;
-
-       if (GET_DWORD(parms[0].info) != _DI_MANU_ID) {
-               Info = _WRONG_MESSAGE_FORMAT;
-       }
-       command = GET_WORD(parms[1].info);
-       m = &parms[2];
-       if (!Info)
-       {
-               switch (command) {
-               case _DI_ASSIGN_PLCI:
-                       if (api_parse(&m->info[1], (word)m->length, "wbbs", m_parms)) {
-                               Info = _WRONG_MESSAGE_FORMAT;
-                               break;
-                       }
-                       codec = GET_WORD(m_parms[0].info);
-                       ch = m_parms[1].info[0];
-                       dir = m_parms[2].info[0];
-                       if ((i = get_plci(a))) {
-                               plci = &a->plci[i - 1];
-                               plci->appl = appl;
-                               plci->command = _MANUFACTURER_R;
-                               plci->m_command = command;
-                               plci->number = Number;
-                               plci->State = LOCAL_CONNECT;
-                               Id = (((word)plci->Id << 8) | plci->adapter->Id | 0x80);
-                               dbug(1, dprintf("ManCMD,plci=0x%x", Id));
-
-                               if ((ch == 1 || ch == 2) && (dir <= 2)) {
-                                       chi[1] = (byte)(0x80 | ch);
-                                       lli[1] = 0;
-                                       plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-                                       switch (codec)
-                                       {
-                                       case 0:
-                                               Info = add_b1(plci, &m_parms[3], 0, 0);
-                                               break;
-                                       case 1:
-                                               add_p(plci, CAI, codec_cai);
-                                               break;
-                                               /* manual 'swich on' to the codec support without signalling */
-                                               /* first 'assign plci' with this function, then use */
-                                       case 2:
-                                               if (AdvCodecSupport(a, plci, appl, 0)) {
-                                                       Info = _RESOURCE_ERROR;
-                                               }
-                                               else {
-                                                       Info = add_b1(plci, &null_parms, 0, B1_FACILITY_LOCAL);
-                                                       lli[1] = 0x10; /* local call codec stream */
-                                               }
-                                               break;
-                                       }
-
-                                       plci->State = LOCAL_CONNECT;
-                                       plci->manufacturer = true;
-                                       plci->command = _MANUFACTURER_R;
-                                       plci->m_command = command;
-                                       plci->number = Number;
-
-                                       if (!Info)
-                                       {
-                                               add_p(plci, LLI, lli);
-                                               add_p(plci, CHI, chi);
-                                               add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               sig_req(plci, ASSIGN, DSIG_ID);
-
-                                               if (!codec)
-                                               {
-                                                       Info = add_b23(plci, &m_parms[3]);
-                                                       if (!Info)
-                                                       {
-                                                               nl_req_ncci(plci, ASSIGN, 0);
-                                                               send_req(plci);
-                                                       }
-                                               }
-                                               if (!Info)
-                                               {
-                                                       dbug(1, dprintf("dir=0x%x,spoof=0x%x", dir, plci->spoofed_msg));
-                                                       if (plci->spoofed_msg == SPOOFING_REQUIRED)
-                                                       {
-                                                               api_save_msg(m_parms, "wbbs", &plci->saved_msg);
-                                                               plci->spoofed_msg = AWAITING_MANUF_CON;
-                                                               plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
-                                                               plci->command = 0;
-                                                               send_req(plci);
-                                                               return false;
-                                                       }
-                                                       if (dir == 1) {
-                                                               sig_req(plci, CALL_REQ, 0);
-                                                       }
-                                                       else if (!dir) {
-                                                               sig_req(plci, LISTEN_REQ, 0);
-                                                       }
-                                                       send_req(plci);
-                                               }
-                                               else
-                                               {
-                                                       sendf(appl,
-                                                             _MANUFACTURER_R | CONFIRM,
-                                                             Id,
-                                                             Number,
-                                                             "dww", _DI_MANU_ID, command, Info);
-                                                       return 2;
-                                               }
-                                       }
-                               }
-                       }
-                       else  Info = _OUT_OF_PLCI;
-                       break;
-
-               case _DI_IDI_CTRL:
-                       if (!plci)
-                       {
-                               Info = _WRONG_IDENTIFIER;
-                               break;
-                       }
-                       if (api_parse(&m->info[1], (word)m->length, "bs", m_parms)) {
-                               Info = _WRONG_MESSAGE_FORMAT;
-                               break;
-                       }
-                       req = m_parms[0].info[0];
-                       plci->command = _MANUFACTURER_R;
-                       plci->m_command = command;
-                       plci->number = Number;
-                       if (req == CALL_REQ)
-                       {
-                               plci->b_channel = getChannel(&m_parms[1]);
-                               mixer_set_bchannel_id_esc(plci, plci->b_channel);
-                               if (plci->spoofed_msg == SPOOFING_REQUIRED)
-                               {
-                                       plci->spoofed_msg = CALL_REQ | AWAITING_MANUF_CON;
-                                       plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
-                                       plci->command = 0;
-                                       break;
-                               }
-                       }
-                       else if (req == LAW_REQ)
-                       {
-                               plci->cr_enquiry = true;
-                       }
-                       add_ss(plci, FTY, &m_parms[1]);
-                       sig_req(plci, req, 0);
-                       send_req(plci);
-                       if (req == HANGUP)
-                       {
-                               if (plci->NL.Id && !plci->nl_remove_id)
-                               {
-                                       if (plci->channels)
-                                       {
-                                               for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
-                                               {
-                                                       if ((a->ncci_plci[ncci] == plci->Id) && (a->ncci_state[ncci] == CONNECTED))
-                                                       {
-                                                               a->ncci_state[ncci] = OUTG_DIS_PENDING;
-                                                               cleanup_ncci_data(plci, ncci);
-                                                               nl_req_ncci(plci, N_DISC, (byte)ncci);
-                                                       }
-                                               }
-                                       }
-                                       mixer_remove(plci);
-                                       nl_req_ncci(plci, REMOVE, 0);
-                                       send_req(plci);
-                               }
-                       }
-                       break;
-
-               case _DI_SIG_CTRL:
-                       /* signalling control for loop activation B-channel */
-                       if (!plci)
-                       {
-                               Info = _WRONG_IDENTIFIER;
-                               break;
-                       }
-                       if (m->length) {
-                               plci->command = _MANUFACTURER_R;
-                               plci->number = Number;
-                               add_ss(plci, FTY, m);
-                               sig_req(plci, SIG_CTRL, 0);
-                               send_req(plci);
-                       }
-                       else Info = _WRONG_MESSAGE_FORMAT;
-                       break;
-
-               case _DI_RXT_CTRL:
-                       /* activation control for receiver/transmitter B-channel */
-                       if (!plci)
-                       {
-                               Info = _WRONG_IDENTIFIER;
-                               break;
-                       }
-                       if (m->length) {
-                               plci->command = _MANUFACTURER_R;
-                               plci->number = Number;
-                               add_ss(plci, FTY, m);
-                               sig_req(plci, DSP_CTRL, 0);
-                               send_req(plci);
-                       }
-                       else Info = _WRONG_MESSAGE_FORMAT;
-                       break;
-
-               case _DI_ADV_CODEC:
-               case _DI_DSP_CTRL:
-                       /* TEL_CTRL commands to support non standard adjustments: */
-                       /* Ring on/off, Handset micro volume, external micro vol. */
-                       /* handset+external speaker volume, receiver+transm. gain,*/
-                       /* handsfree on (hookinfo off), set mixer command         */
-
-                       if (command == _DI_ADV_CODEC)
-                       {
-                               if (!a->AdvCodecPLCI) {
-                                       Info = _WRONG_STATE;
-                                       break;
-                               }
-                               v_plci = a->AdvCodecPLCI;
-                       }
-                       else
-                       {
-                               if (plci
-                                   && (m->length >= 3)
-                                   && (m->info[1] == 0x1c)
-                                   && (m->info[2] >= 1))
-                               {
-                                       if (m->info[3] == DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS)
-                                       {
-                                               if ((plci->tel != ADV_VOICE) || (plci != a->AdvSignalPLCI))
-                                               {
-                                                       Info = _WRONG_STATE;
-                                                       break;
-                                               }
-                                               a->adv_voice_coef_length = m->info[2] - 1;
-                                               if (a->adv_voice_coef_length > m->length - 3)
-                                                       a->adv_voice_coef_length = (byte)(m->length - 3);
-                                               if (a->adv_voice_coef_length > ADV_VOICE_COEF_BUFFER_SIZE)
-                                                       a->adv_voice_coef_length = ADV_VOICE_COEF_BUFFER_SIZE;
-                                               for (i = 0; i < a->adv_voice_coef_length; i++)
-                                                       a->adv_voice_coef_buffer[i] = m->info[4 + i];
-                                               if (plci->B1_facilities & B1_FACILITY_VOICE)
-                                                       adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE);
-                                               break;
-                                       }
-                                       else if (m->info[3] == DSP_CTRL_SET_DTMF_PARAMETERS)
-                                       {
-                                               if (!(a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_PARAMETERS))
-                                               {
-                                                       Info = _FACILITY_NOT_SUPPORTED;
-                                                       break;
-                                               }
-
-                                               plci->dtmf_parameter_length = m->info[2] - 1;
-                                               if (plci->dtmf_parameter_length > m->length - 3)
-                                                       plci->dtmf_parameter_length = (byte)(m->length - 3);
-                                               if (plci->dtmf_parameter_length > DTMF_PARAMETER_BUFFER_SIZE)
-                                                       plci->dtmf_parameter_length = DTMF_PARAMETER_BUFFER_SIZE;
-                                               for (i = 0; i < plci->dtmf_parameter_length; i++)
-                                                       plci->dtmf_parameter_buffer[i] = m->info[4 + i];
-                                               if (plci->B1_facilities & B1_FACILITY_DTMFR)
-                                                       dtmf_parameter_write(plci);
-                                               break;
-
-                                       }
-                               }
-                               v_plci = plci;
-                       }
-
-                       if (!v_plci)
-                       {
-                               Info = _WRONG_IDENTIFIER;
-                               break;
-                       }
-                       if (m->length) {
-                               add_ss(v_plci, FTY, m);
-                               sig_req(v_plci, TEL_CTRL, 0);
-                               send_req(v_plci);
-                       }
-                       else Info = _WRONG_MESSAGE_FORMAT;
-
-                       break;
-
-               case _DI_OPTIONS_REQUEST:
-                       if (api_parse(&m->info[1], (word)m->length, "d", m_parms)) {
-                               Info = _WRONG_MESSAGE_FORMAT;
-                               break;
-                       }
-                       if (GET_DWORD(m_parms[0].info) & ~a->man_profile.private_options)
-                       {
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       a->requested_options_table[appl->Id - 1] = GET_DWORD(m_parms[0].info);
-                       break;
-
-
-
-               default:
-                       Info = _WRONG_MESSAGE_FORMAT;
-                       break;
-               }
-       }
-
-       sendf(appl,
-             _MANUFACTURER_R | CONFIRM,
-             Id,
-             Number,
-             "dww", _DI_MANU_ID, command, Info);
-       return false;
-}
-
-
-static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
-                            PLCI *plci, APPL *appl, API_PARSE *msg)
-{
-       word indication;
-
-       API_PARSE m_parms[3];
-       API_PARSE *ncpi;
-       API_PARSE fax_parms[9];
-       word i;
-       byte len;
-
-
-       dbug(1, dprintf("manufacturer_res"));
-
-       if ((msg[0].length == 0)
-           || (msg[1].length == 0)
-           || (GET_DWORD(msg[0].info) != _DI_MANU_ID))
-       {
-               return false;
-       }
-       indication = GET_WORD(msg[1].info);
-       switch (indication)
-       {
-
-       case _DI_NEGOTIATE_B3:
-               if (!plci)
-                       break;
-               if (((plci->B3_prot != 4) && (plci->B3_prot != 5))
-                   || !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
-               {
-                       dbug(1, dprintf("wrong state for NEGOTIATE_B3 parameters"));
-                       break;
-               }
-               if (api_parse(&msg[2].info[1], msg[2].length, "ws", m_parms))
-               {
-                       dbug(1, dprintf("wrong format in NEGOTIATE_B3 parameters"));
-                       break;
-               }
-               ncpi = &m_parms[1];
-               len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
-               if (plci->fax_connect_info_length < len)
-               {
-                       ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
-                       ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
-               }
-               if (api_parse(&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
-               {
-                       dbug(1, dprintf("non-standard facilities info missing or wrong format"));
-               }
-               else
-               {
-                       if (plci->fax_connect_info_length <= len)
-                               plci->fax_connect_info_buffer[len] = 0;
-                       len += 1 + plci->fax_connect_info_buffer[len];
-                       if (plci->fax_connect_info_length <= len)
-                               plci->fax_connect_info_buffer[len] = 0;
-                       len += 1 + plci->fax_connect_info_buffer[len];
-                       if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
-                               plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
-                       plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
-                       for (i = 0; i < fax_parms[7].length; i++)
-                               plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1 + i];
-               }
-               plci->fax_connect_info_length = len;
-               plci->fax_edata_ack_length = plci->fax_connect_info_length;
-               start_internal_command(Id, plci, fax_edata_ack_command);
-               break;
-
-       }
-       return false;
-}
-
-/*------------------------------------------------------------------*/
-/* IDI callback function                                            */
-/*------------------------------------------------------------------*/
-
-void callback(ENTITY *e)
-{
-       DIVA_CAPI_ADAPTER *a;
-       APPL *appl;
-       PLCI *plci;
-       CAPI_MSG *m;
-       word i, j;
-       byte rc;
-       byte ch;
-       byte req;
-       byte global_req;
-       int no_cancel_rc;
-
-       dbug(1, dprintf("%x:CB(%x:Req=%x,Rc=%x,Ind=%x)",
-                       (e->user[0] + 1) & 0x7fff, e->Id, e->Req, e->Rc, e->Ind));
-
-       a = &(adapter[(byte)e->user[0]]);
-       plci = &(a->plci[e->user[1]]);
-       no_cancel_rc = DIVA_CAPI_SUPPORTS_NO_CANCEL(a);
-
-       /*
-         If new protocol code and new XDI is used then CAPI should work
-         fully in accordance with IDI cpec an look on callback field instead
-         of Rc field for return codes.
-       */
-       if (((e->complete == 0xff) && no_cancel_rc) ||
-           (e->Rc && !no_cancel_rc)) {
-               rc = e->Rc;
-               ch = e->RcCh;
-               req = e->Req;
-               e->Rc = 0;
-
-               if (e->user[0] & 0x8000)
-               {
-                       /*
-                         If REMOVE request was sent then we have to wait until
-                         return code with Id set to zero arrives.
-                         All other return codes should be ignored.
-                       */
-                       if (req == REMOVE)
-                       {
-                               if (e->Id)
-                               {
-                                       dbug(1, dprintf("cancel RC in REMOVE state"));
-                                       return;
-                               }
-                               channel_flow_control_remove(plci);
-                               for (i = 0; i < 256; i++)
-                               {
-                                       if (a->FlowControlIdTable[i] == plci->nl_remove_id)
-                                               a->FlowControlIdTable[i] = 0;
-                               }
-                               plci->nl_remove_id = 0;
-                               if (plci->rx_dma_descriptor > 0) {
-                                       diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1);
-                                       plci->rx_dma_descriptor = 0;
-                               }
-                       }
-                       if (rc == OK_FC)
-                       {
-                               a->FlowControlIdTable[ch] = e->Id;
-                               a->FlowControlSkipTable[ch] = 0;
-
-                               a->ch_flow_control[ch] |= N_OK_FC_PENDING;
-                               a->ch_flow_plci[ch] = plci->Id;
-                               plci->nl_req = 0;
-                       }
-                       else
-                       {
-                               /*
-                                 Cancel return codes self, if feature was requested
-                               */
-                               if (no_cancel_rc && (a->FlowControlIdTable[ch] == e->Id) && e->Id) {
-                                       a->FlowControlIdTable[ch] = 0;
-                                       if ((rc == OK) && a->FlowControlSkipTable[ch]) {
-                                               dbug(3, dprintf("XDI CAPI: RC cancelled Id:0x02, Ch:%02x", e->Id, ch));
-                                               return;
-                                       }
-                               }
-
-                               if (a->ch_flow_control[ch] & N_OK_FC_PENDING)
-                               {
-                                       a->ch_flow_control[ch] &= ~N_OK_FC_PENDING;
-                                       if (ch == e->ReqCh)
-                                               plci->nl_req = 0;
-                               }
-                               else
-                                       plci->nl_req = 0;
-                       }
-                       if (plci->nl_req)
-                               control_rc(plci, 0, rc, ch, 0, true);
-                       else
-                       {
-                               if (req == N_XON)
-                               {
-                                       channel_x_on(plci, ch);
-                                       if (plci->internal_command)
-                                               control_rc(plci, req, rc, ch, 0, true);
-                               }
-                               else
-                               {
-                                       if (plci->nl_global_req)
-                                       {
-                                               global_req = plci->nl_global_req;
-                                               plci->nl_global_req = 0;
-                                               if (rc != ASSIGN_OK) {
-                                                       e->Id = 0;
-                                                       if (plci->rx_dma_descriptor > 0) {
-                                                               diva_free_dma_descriptor(plci, plci->rx_dma_descriptor - 1);
-                                                               plci->rx_dma_descriptor = 0;
-                                                       }
-                                               }
-                                               channel_xmit_xon(plci);
-                                               control_rc(plci, 0, rc, ch, global_req, true);
-                                       }
-                                       else if (plci->data_sent)
-                                       {
-                                               channel_xmit_xon(plci);
-                                               plci->data_sent = false;
-                                               plci->NL.XNum = 1;
-                                               data_rc(plci, ch);
-                                               if (plci->internal_command)
-                                                       control_rc(plci, req, rc, ch, 0, true);
-                                       }
-                                       else
-                                       {
-                                               channel_xmit_xon(plci);
-                                               control_rc(plci, req, rc, ch, 0, true);
-                                       }
-                               }
-                       }
-               }
-               else
-               {
-                       /*
-                         If REMOVE request was sent then we have to wait until
-                         return code with Id set to zero arrives.
-                         All other return codes should be ignored.
-                       */
-                       if (req == REMOVE)
-                       {
-                               if (e->Id)
-                               {
-                                       dbug(1, dprintf("cancel RC in REMOVE state"));
-                                       return;
-                               }
-                               plci->sig_remove_id = 0;
-                       }
-                       plci->sig_req = 0;
-                       if (plci->sig_global_req)
-                       {
-                               global_req = plci->sig_global_req;
-                               plci->sig_global_req = 0;
-                               if (rc != ASSIGN_OK)
-                                       e->Id = 0;
-                               channel_xmit_xon(plci);
-                               control_rc(plci, 0, rc, ch, global_req, false);
-                       }
-                       else
-                       {
-                               channel_xmit_xon(plci);
-                               control_rc(plci, req, rc, ch, 0, false);
-                       }
-               }
-               /*
-                 Again: in accordance with IDI spec Rc and Ind can't be delivered in the
-                 same callback. Also if new XDI and protocol code used then jump
-                 direct to finish.
-               */
-               if (no_cancel_rc) {
-                       channel_xmit_xon(plci);
-                       goto capi_callback_suffix;
-               }
-       }
-
-       channel_xmit_xon(plci);
-
-       if (e->Ind) {
-               if (e->user[0] & 0x8000) {
-                       byte Ind = e->Ind & 0x0f;
-                       byte Ch = e->IndCh;
-                       if (((Ind == N_DISC) || (Ind == N_DISC_ACK)) &&
-                           (a->ch_flow_plci[Ch] == plci->Id)) {
-                               if (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK) {
-                                       dbug(3, dprintf("XDI CAPI: I: pending N-XON Ch:%02x", Ch));
-                               }
-                               a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
-                       }
-                       nl_ind(plci);
-                       if ((e->RNR != 1) &&
-                           (a->ch_flow_plci[Ch] == plci->Id) &&
-                           (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK)) {
-                               a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
-                               dbug(3, dprintf("XDI CAPI: I: remove faked N-XON Ch:%02x", Ch));
-                       }
-               } else {
-                       sig_ind(plci);
-               }
-               e->Ind = 0;
-       }
-
-capi_callback_suffix:
-
-       while (!plci->req_in
-              && !plci->internal_command
-              && (plci->msg_in_write_pos != plci->msg_in_read_pos))
-       {
-               j = (plci->msg_in_read_pos == plci->msg_in_wrap_pos) ? 0 : plci->msg_in_read_pos;
-
-               i = (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]))->header.length + 3) & 0xfffc;
-
-               m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
-               appl = *((APPL **)(&((byte *)(plci->msg_in_queue))[j + i]));
-               dbug(1, dprintf("dequeue msg(0x%04x) - write=%d read=%d wrap=%d",
-                               m->header.command, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos));
-               if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
-               {
-                       plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
-                       plci->msg_in_read_pos = i + MSG_IN_OVERHEAD;
-               }
-               else
-               {
-                       plci->msg_in_read_pos = j + i + MSG_IN_OVERHEAD;
-               }
-               if (plci->msg_in_read_pos == plci->msg_in_write_pos)
-               {
-                       plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
-                       plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
-               }
-               else if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
-               {
-                       plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
-                       plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
-               }
-               i = api_put(appl, m);
-               if (i != 0)
-               {
-                       if (m->header.command == _DATA_B3_R)
-
-                               TransmitBufferFree(appl, (byte *)(long)(m->info.data_b3_req.Data));
-
-                       dbug(1, dprintf("Error 0x%04x from msg(0x%04x)", i, m->header.command));
-                       break;
-               }
-
-               if (plci->li_notify_update)
-               {
-                       plci->li_notify_update = false;
-                       mixer_notify_update(plci, false);
-               }
-
-       }
-       send_data(plci);
-       send_req(plci);
-}
-
-
-static void control_rc(PLCI *plci, byte req, byte rc, byte ch, byte global_req,
-                      byte nl_rc)
-{
-       dword Id;
-       dword rId;
-       word Number;
-       word Info = 0;
-       word i;
-       word ncci;
-       DIVA_CAPI_ADAPTER *a;
-       APPL *appl;
-       PLCI *rplci;
-       byte SSparms[] = "\x05\x00\x00\x02\x00\x00";
-       byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
-
-       if (!plci) {
-               dbug(0, dprintf("A: control_rc, no plci %02x:%02x:%02x:%02x:%02x", req, rc, ch, global_req, nl_rc));
-               return;
-       }
-       dbug(1, dprintf("req0_in/out=%d/%d", plci->req_in, plci->req_out));
-       if (plci->req_in != plci->req_out)
-       {
-               if (nl_rc || (global_req != ASSIGN) || (rc == ASSIGN_OK))
-               {
-                       dbug(1, dprintf("req_1return"));
-                       return;
-               }
-               /* cancel outstanding request on the PLCI after SIG ASSIGN failure */
-       }
-       plci->req_in = plci->req_in_start = plci->req_out = 0;
-       dbug(1, dprintf("control_rc"));
-
-       appl = plci->appl;
-       a = plci->adapter;
-       ncci = a->ch_ncci[ch];
-       if (appl)
-       {
-               Id = (((dword)(ncci ? ncci : ch)) << 16) | ((word)plci->Id << 8) | a->Id;
-               if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER;
-               Number = plci->number;
-               dbug(1, dprintf("Contr_RC-Id=%08lx,plci=%x,tel=%x, entity=0x%x, command=0x%x, int_command=0x%x", Id, plci->Id, plci->tel, plci->Sig.Id, plci->command, plci->internal_command));
-               dbug(1, dprintf("channels=0x%x", plci->channels));
-               if (plci_remove_check(plci))
-                       return;
-               if (req == REMOVE && rc == ASSIGN_OK)
-               {
-                       sig_req(plci, HANGUP, 0);
-                       sig_req(plci, REMOVE, 0);
-                       send_req(plci);
-               }
-               if (plci->command)
-               {
-                       switch (plci->command)
-                       {
-                       case C_HOLD_REQ:
-                               dbug(1, dprintf("HoldRC=0x%x", rc));
-                               SSparms[1] = (byte)S_HOLD;
-                               if (rc != OK)
-                               {
-                                       plci->SuppState = IDLE;
-                                       Info = 0x2001;
-                               }
-                               sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms);
-                               break;
-
-                       case C_RETRIEVE_REQ:
-                               dbug(1, dprintf("RetrieveRC=0x%x", rc));
-                               SSparms[1] = (byte)S_RETRIEVE;
-                               if (rc != OK)
-                               {
-                                       plci->SuppState = CALL_HELD;
-                                       Info = 0x2001;
-                               }
-                               sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", Info, 3, SSparms);
-                               break;
-
-                       case _INFO_R:
-                               dbug(1, dprintf("InfoRC=0x%x", rc));
-                               if (rc != OK) Info = _WRONG_STATE;
-                               sendf(appl, _INFO_R | CONFIRM, Id, Number, "w", Info);
-                               break;
-
-                       case _CONNECT_R:
-                               dbug(1, dprintf("Connect_R=0x%x/0x%x/0x%x/0x%x", req, rc, global_req, nl_rc));
-                               if (plci->State == INC_DIS_PENDING)
-                                       break;
-                               if (plci->Sig.Id != 0xff)
-                               {
-                                       if (((global_req == ASSIGN) && (rc != ASSIGN_OK))
-                                           || (!nl_rc && (req == CALL_REQ) && (rc != OK)))
-                                       {
-                                               dbug(1, dprintf("No more IDs/Call_Req failed"));
-                                               sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
-                                               plci_remove(plci);
-                                               plci->State = IDLE;
-                                               break;
-                                       }
-                                       if (plci->State != LOCAL_CONNECT) plci->State = OUTG_CON_PENDING;
-                                       sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
-                               }
-                               else /* D-ch activation */
-                               {
-                                       if (rc != ASSIGN_OK)
-                                       {
-                                               dbug(1, dprintf("No more IDs/X.25 Call_Req failed"));
-                                               sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
-                                               plci_remove(plci);
-                                               plci->State = IDLE;
-                                               break;
-                                       }
-                                       sendf(appl, _CONNECT_R | CONFIRM, Id, Number, "w", 0);
-                                       sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "sss", "", "", "");
-                                       plci->State = INC_ACT_PENDING;
-                               }
-                               break;
-
-                       case _CONNECT_I | RESPONSE:
-                               if (plci->State != INC_DIS_PENDING)
-                                       plci->State = INC_CON_ACCEPT;
-                               break;
-
-                       case _DISCONNECT_R:
-                               if (plci->State == INC_DIS_PENDING)
-                                       break;
-                               if (plci->Sig.Id != 0xff)
-                               {
-                                       plci->State = OUTG_DIS_PENDING;
-                                       sendf(appl, _DISCONNECT_R | CONFIRM, Id, Number, "w", 0);
-                               }
-                               break;
-
-                       case SUSPEND_REQ:
-                               break;
-
-                       case RESUME_REQ:
-                               break;
-
-                       case _CONNECT_B3_R:
-                               if (rc != OK)
-                               {
-                                       sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
-                                       break;
-                               }
-                               ncci = get_ncci(plci, ch, 0);
-                               Id = (Id & 0xffff) | (((dword) ncci) << 16);
-                               plci->channels++;
-                               if (req == N_RESET)
-                               {
-                                       a->ncci_state[ncci] = INC_ACT_PENDING;
-                                       sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
-                                       sendf(appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                               }
-                               else
-                               {
-                                       a->ncci_state[ncci] = OUTG_CON_PENDING;
-                                       sendf(appl, _CONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
-                               }
-                               break;
-
-                       case _CONNECT_B3_I | RESPONSE:
-                               break;
-
-                       case _RESET_B3_R:
-/*        sendf(appl, _RESET_B3_R | CONFIRM, Id, Number, "w", 0);*/
-                               break;
-
-                       case _DISCONNECT_B3_R:
-                               sendf(appl, _DISCONNECT_B3_R | CONFIRM, Id, Number, "w", 0);
-                               break;
-
-                       case _MANUFACTURER_R:
-                               break;
-
-                       case PERM_LIST_REQ:
-                               if (rc != OK)
-                               {
-                                       Info = _WRONG_IDENTIFIER;
-                                       sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info);
-                                       plci_remove(plci);
-                               }
-                               else
-                                       sendf(plci->appl, _CONNECT_R | CONFIRM, Id, Number, "w", Info);
-                               break;
-
-                       default:
-                               break;
-                       }
-                       plci->command = 0;
-               }
-               else if (plci->internal_command)
-               {
-                       switch (plci->internal_command)
-                       {
-                       case BLOCK_PLCI:
-                               return;
-
-                       case GET_MWI_STATE:
-                               if (rc == OK) /* command supported, wait for indication */
-                               {
-                                       return;
-                               }
-                               plci_remove(plci);
-                               break;
-
-                               /* Get Supported Services */
-                       case GETSERV_REQ_PEND:
-                               if (rc == OK) /* command supported, wait for indication */
-                               {
-                                       break;
-                               }
-                               PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
-                               sendf(appl, _FACILITY_R | CONFIRM, Id, Number, "wws", 0, 3, SSstruct);
-                               plci_remove(plci);
-                               break;
-
-                       case INTERR_DIVERSION_REQ_PEND:      /* Interrogate Parameters        */
-                       case INTERR_NUMBERS_REQ_PEND:
-                       case CF_START_PEND:                  /* Call Forwarding Start pending */
-                       case CF_STOP_PEND:                   /* Call Forwarding Stop pending  */
-                       case CCBS_REQUEST_REQ_PEND:
-                       case CCBS_DEACTIVATE_REQ_PEND:
-                       case CCBS_INTERROGATE_REQ_PEND:
-                               switch (plci->internal_command)
-                               {
-                               case INTERR_DIVERSION_REQ_PEND:
-                                       SSparms[1] = S_INTERROGATE_DIVERSION;
-                                       break;
-                               case INTERR_NUMBERS_REQ_PEND:
-                                       SSparms[1] = S_INTERROGATE_NUMBERS;
-                                       break;
-                               case CF_START_PEND:
-                                       SSparms[1] = S_CALL_FORWARDING_START;
-                                       break;
-                               case CF_STOP_PEND:
-                                       SSparms[1] = S_CALL_FORWARDING_STOP;
-                                       break;
-                               case CCBS_REQUEST_REQ_PEND:
-                                       SSparms[1] = S_CCBS_REQUEST;
-                                       break;
-                               case CCBS_DEACTIVATE_REQ_PEND:
-                                       SSparms[1] = S_CCBS_DEACTIVATE;
-                                       break;
-                               case CCBS_INTERROGATE_REQ_PEND:
-                                       SSparms[1] = S_CCBS_INTERROGATE;
-                                       break;
-                               }
-                               if (global_req == ASSIGN)
-                               {
-                                       dbug(1, dprintf("AssignDiversion_RC=0x%x/0x%x", req, rc));
-                                       return;
-                               }
-                               if (!plci->appl) break;
-                               if (rc == ISDN_GUARD_REJ)
-                               {
-                                       Info = _CAPI_GUARD_ERROR;
-                               }
-                               else if (rc != OK)
-                               {
-                                       Info = _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED;
-                               }
-                               sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7,
-                                     plci->number, "wws", Info, (word)3, SSparms);
-                               if (Info) plci_remove(plci);
-                               break;
-
-                               /* 3pty conference pending */
-                       case PTY_REQ_PEND:
-                               if (!plci->relatedPTYPLCI) break;
-                               rplci = plci->relatedPTYPLCI;
-                               SSparms[1] = plci->ptyState;
-                               rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
-                               if (rplci->tel) rId |= EXT_CONTROLLER;
-                               if (rc != OK)
-                               {
-                                       Info = 0x300E; /* not supported */
-                                       plci->relatedPTYPLCI = NULL;
-                                       plci->ptyState = 0;
-                               }
-                               sendf(rplci->appl,
-                                     _FACILITY_R | CONFIRM,
-                                     rId,
-                                     plci->number,
-                                     "wws", Info, (word)3, SSparms);
-                               break;
-
-                               /* Explicit Call Transfer pending */
-                       case ECT_REQ_PEND:
-                               dbug(1, dprintf("ECT_RC=0x%x/0x%x", req, rc));
-                               if (!plci->relatedPTYPLCI) break;
-                               rplci = plci->relatedPTYPLCI;
-                               SSparms[1] = S_ECT;
-                               rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
-                               if (rplci->tel) rId |= EXT_CONTROLLER;
-                               if (rc != OK)
-                               {
-                                       Info = 0x300E; /* not supported */
-                                       plci->relatedPTYPLCI = NULL;
-                                       plci->ptyState = 0;
-                               }
-                               sendf(rplci->appl,
-                                     _FACILITY_R | CONFIRM,
-                                     rId,
-                                     plci->number,
-                                     "wws", Info, (word)3, SSparms);
-                               break;
-
-                       case _MANUFACTURER_R:
-                               dbug(1, dprintf("_Manufacturer_R=0x%x/0x%x", req, rc));
-                               if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
-                               {
-                                       dbug(1, dprintf("No more IDs"));
-                                       sendf(appl, _MANUFACTURER_R | CONFIRM, Id, Number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI);
-                                       plci_remove(plci);  /* after codec init, internal codec commands pending */
-                               }
-                               break;
-
-                       case _CONNECT_R:
-                               dbug(1, dprintf("_Connect_R=0x%x/0x%x", req, rc));
-                               if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
-                               {
-                                       dbug(1, dprintf("No more IDs"));
-                                       sendf(appl, _CONNECT_R | CONFIRM, Id & 0xffL, Number, "w", _OUT_OF_PLCI);
-                                       plci_remove(plci);  /* after codec init, internal codec commands pending */
-                               }
-                               break;
-
-                       case PERM_COD_HOOK:                     /* finished with Hook_Ind */
-                               return;
-
-                       case PERM_COD_CALL:
-                               dbug(1, dprintf("***Codec Connect_Pending A, Rc = 0x%x", rc));
-                               plci->internal_command = PERM_COD_CONN_PEND;
-                               return;
-
-                       case PERM_COD_ASSIGN:
-                               dbug(1, dprintf("***Codec Assign A, Rc = 0x%x", rc));
-                               if (rc != ASSIGN_OK) break;
-                               sig_req(plci, CALL_REQ, 0);
-                               send_req(plci);
-                               plci->internal_command = PERM_COD_CALL;
-                               return;
-
-                               /* Null Call Reference Request pending */
-                       case C_NCR_FAC_REQ:
-                               dbug(1, dprintf("NCR_FAC=0x%x/0x%x", req, rc));
-                               if (global_req == ASSIGN)
-                               {
-                                       if (rc == ASSIGN_OK)
-                                       {
-                                               return;
-                                       }
-                                       else
-                                       {
-                                               sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE);
-                                               appl->NullCREnable = false;
-                                               plci_remove(plci);
-                                       }
-                               }
-                               else if (req == NCR_FACILITY)
-                               {
-                                       if (rc == OK)
-                                       {
-                                               sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", 0);
-                                       }
-                                       else
-                                       {
-                                               sendf(appl, _INFO_R | CONFIRM, Id & 0xf, Number, "w", _WRONG_STATE);
-                                               appl->NullCREnable = false;
-                                       }
-                                       plci_remove(plci);
-                               }
-                               break;
-
-                       case HOOK_ON_REQ:
-                               if (plci->channels)
-                               {
-                                       if (a->ncci_state[ncci] == CONNECTED)
-                                       {
-                                               a->ncci_state[ncci] = OUTG_DIS_PENDING;
-                                               cleanup_ncci_data(plci, ncci);
-                                               nl_req_ncci(plci, N_DISC, (byte)ncci);
-                                       }
-                                       break;
-                               }
-                               break;
-
-                       case HOOK_OFF_REQ:
-                               if (plci->State == INC_DIS_PENDING)
-                                       break;
-                               sig_req(plci, CALL_REQ, 0);
-                               send_req(plci);
-                               plci->State = OUTG_CON_PENDING;
-                               break;
-
-
-                       case MWI_ACTIVATE_REQ_PEND:
-                       case MWI_DEACTIVATE_REQ_PEND:
-                               if (global_req == ASSIGN && rc == ASSIGN_OK)
-                               {
-                                       dbug(1, dprintf("MWI_REQ assigned"));
-                                       return;
-                               }
-                               else if (rc != OK)
-                               {
-                                       if (rc == WRONG_IE)
-                                       {
-                                               Info = 0x2007; /* Illegal message parameter coding */
-                                               dbug(1, dprintf("MWI_REQ invalid parameter"));
-                                       }
-                                       else
-                                       {
-                                               Info = 0x300B; /* not supported */
-                                               dbug(1, dprintf("MWI_REQ not supported"));
-                                       }
-                                       /* 0x3010: Request not allowed in this state */
-                                       PUT_WORD(&SSparms[4], 0x300E); /* SS not supported */
-
-                               }
-                               if (plci->internal_command == MWI_ACTIVATE_REQ_PEND)
-                               {
-                                       PUT_WORD(&SSparms[1], S_MWI_ACTIVATE);
-                               }
-                               else PUT_WORD(&SSparms[1], S_MWI_DEACTIVATE);
-
-                               if (plci->cr_enquiry)
-                               {
-                                       sendf(plci->appl,
-                                             _FACILITY_R | CONFIRM,
-                                             Id & 0xf,
-                                             plci->number,
-                                             "wws", Info, (word)3, SSparms);
-                                       if (rc != OK) plci_remove(plci);
-                               }
-                               else
-                               {
-                                       sendf(plci->appl,
-                                             _FACILITY_R | CONFIRM,
-                                             Id,
-                                             plci->number,
-                                             "wws", Info, (word)3, SSparms);
-                               }
-                               break;
-
-                       case CONF_BEGIN_REQ_PEND:
-                       case CONF_ADD_REQ_PEND:
-                       case CONF_SPLIT_REQ_PEND:
-                       case CONF_DROP_REQ_PEND:
-                       case CONF_ISOLATE_REQ_PEND:
-                       case CONF_REATTACH_REQ_PEND:
-                               dbug(1, dprintf("CONF_RC=0x%x/0x%x", req, rc));
-                               if ((plci->internal_command == CONF_ADD_REQ_PEND) && (!plci->relatedPTYPLCI)) break;
-                               rplci = plci;
-                               rId = Id;
-                               switch (plci->internal_command)
-                               {
-                               case CONF_BEGIN_REQ_PEND:
-                                       SSparms[1] = S_CONF_BEGIN;
-                                       break;
-                               case CONF_ADD_REQ_PEND:
-                                       SSparms[1] = S_CONF_ADD;
-                                       rplci = plci->relatedPTYPLCI;
-                                       rId = ((word)rplci->Id << 8) | rplci->adapter->Id;
-                                       break;
-                               case CONF_SPLIT_REQ_PEND:
-                                       SSparms[1] = S_CONF_SPLIT;
-                                       break;
-                               case CONF_DROP_REQ_PEND:
-                                       SSparms[1] = S_CONF_DROP;
-                                       break;
-                               case CONF_ISOLATE_REQ_PEND:
-                                       SSparms[1] = S_CONF_ISOLATE;
-                                       break;
-                               case CONF_REATTACH_REQ_PEND:
-                                       SSparms[1] = S_CONF_REATTACH;
-                                       break;
-                               }
-
-                               if (rc != OK)
-                               {
-                                       Info = 0x300E; /* not supported */
-                                       plci->relatedPTYPLCI = NULL;
-                                       plci->ptyState = 0;
-                               }
-                               sendf(rplci->appl,
-                                     _FACILITY_R | CONFIRM,
-                                     rId,
-                                     plci->number,
-                                     "wws", Info, (word)3, SSparms);
-                               break;
-
-                       case VSWITCH_REQ_PEND:
-                               if (rc != OK)
-                               {
-                                       if (plci->relatedPTYPLCI)
-                                       {
-                                               plci->relatedPTYPLCI->vswitchstate = 0;
-                                               plci->relatedPTYPLCI->vsprot = 0;
-                                               plci->relatedPTYPLCI->vsprotdialect = 0;
-                                       }
-                                       plci->vswitchstate = 0;
-                                       plci->vsprot = 0;
-                                       plci->vsprotdialect = 0;
-                               }
-                               else
-                               {
-                                       if (plci->relatedPTYPLCI &&
-                                           plci->vswitchstate == 1 &&
-                                           plci->relatedPTYPLCI->vswitchstate == 3) /* join complete */
-                                               plci->vswitchstate = 3;
-                               }
-                               break;
-
-                               /* Call Deflection Request pending (SSCT) */
-                       case CD_REQ_PEND:
-                               SSparms[1] = S_CALL_DEFLECTION;
-                               if (rc != OK)
-                               {
-                                       Info = 0x300E; /* not supported */
-                                       plci->appl->CDEnable = 0;
-                               }
-                               sendf(plci->appl, _FACILITY_R | CONFIRM, Id,
-                                     plci->number, "wws", Info, (word)3, SSparms);
-                               break;
-
-                       case RTP_CONNECT_B3_REQ_COMMAND_2:
-                               if (rc == OK)
-                               {
-                                       ncci = get_ncci(plci, ch, 0);
-                                       Id = (Id & 0xffff) | (((dword) ncci) << 16);
-                                       plci->channels++;
-                                       a->ncci_state[ncci] = OUTG_CON_PENDING;
-                               }
-                               /* fall through */
-
-                       default:
-                               if (plci->internal_command_queue[0])
-                               {
-                                       (*(plci->internal_command_queue[0]))(Id, plci, rc);
-                                       if (plci->internal_command)
-                                               return;
-                               }
-                               break;
-                       }
-                       next_internal_command(Id, plci);
-               }
-       }
-       else /* appl==0 */
-       {
-               Id = ((word)plci->Id << 8) | plci->adapter->Id;
-               if (plci->tel) Id |= EXT_CONTROLLER;
-
-               switch (plci->internal_command)
-               {
-               case BLOCK_PLCI:
-                       return;
-
-               case START_L1_SIG_ASSIGN_PEND:
-               case REM_L1_SIG_ASSIGN_PEND:
-                       if (global_req == ASSIGN)
-                       {
-                               break;
-                       }
-                       else
-                       {
-                               dbug(1, dprintf("***L1 Req rem PLCI"));
-                               plci->internal_command = 0;
-                               sig_req(plci, REMOVE, 0);
-                               send_req(plci);
-                       }
-                       break;
-
-                       /* Call Deflection Request pending, just no appl ptr assigned */
-               case CD_REQ_PEND:
-                       SSparms[1] = S_CALL_DEFLECTION;
-                       if (rc != OK)
-                       {
-                               Info = 0x300E; /* not supported */
-                       }
-                       for (i = 0; i < max_appl; i++)
-                       {
-                               if (application[i].CDEnable)
-                               {
-                                       if (!application[i].Id) application[i].CDEnable = 0;
-                                       else
-                                       {
-                                               sendf(&application[i], _FACILITY_R | CONFIRM, Id,
-                                                     plci->number, "wws", Info, (word)3, SSparms);
-                                               if (Info) application[i].CDEnable = 0;
-                                       }
-                               }
-                       }
-                       plci->internal_command = 0;
-                       break;
-
-               case PERM_COD_HOOK:                   /* finished with Hook_Ind */
-                       return;
-
-               case PERM_COD_CALL:
-                       plci->internal_command = PERM_COD_CONN_PEND;
-                       dbug(1, dprintf("***Codec Connect_Pending, Rc = 0x%x", rc));
-                       return;
-
-               case PERM_COD_ASSIGN:
-                       dbug(1, dprintf("***Codec Assign, Rc = 0x%x", rc));
-                       plci->internal_command = 0;
-                       if (rc != ASSIGN_OK) break;
-                       plci->internal_command = PERM_COD_CALL;
-                       sig_req(plci, CALL_REQ, 0);
-                       send_req(plci);
-                       return;
-
-               case LISTEN_SIG_ASSIGN_PEND:
-                       if (rc == ASSIGN_OK)
-                       {
-                               plci->internal_command = 0;
-                               dbug(1, dprintf("ListenCheck, new SIG_ID = 0x%x", plci->Sig.Id));
-                               add_p(plci, ESC, "\x02\x18\x00");             /* support call waiting */
-                               sig_req(plci, INDICATE_REQ, 0);
-                               send_req(plci);
-                       }
-                       else
-                       {
-                               dbug(1, dprintf("ListenCheck failed (assignRc=0x%x)", rc));
-                               a->listen_active--;
-                               plci_remove(plci);
-                               plci->State = IDLE;
-                       }
-                       break;
-
-               case USELAW_REQ:
-                       if (global_req == ASSIGN)
-                       {
-                               if (rc == ASSIGN_OK)
-                               {
-                                       sig_req(plci, LAW_REQ, 0);
-                                       send_req(plci);
-                                       dbug(1, dprintf("Auto-Law assigned"));
-                               }
-                               else
-                               {
-                                       dbug(1, dprintf("Auto-Law assign failed"));
-                                       a->automatic_law = 3;
-                                       plci->internal_command = 0;
-                                       a->automatic_lawPLCI = NULL;
-                               }
-                               break;
-                       }
-                       else if (req == LAW_REQ && rc == OK)
-                       {
-                               dbug(1, dprintf("Auto-Law initiated"));
-                               a->automatic_law = 2;
-                               plci->internal_command = 0;
-                       }
-                       else
-                       {
-                               dbug(1, dprintf("Auto-Law not supported"));
-                               a->automatic_law = 3;
-                               plci->internal_command = 0;
-                               sig_req(plci, REMOVE, 0);
-                               send_req(plci);
-                               a->automatic_lawPLCI = NULL;
-                       }
-                       break;
-               }
-               plci_remove_check(plci);
-       }
-}
-
-static void data_rc(PLCI *plci, byte ch)
-{
-       dword Id;
-       DIVA_CAPI_ADAPTER *a;
-       NCCI *ncci_ptr;
-       DATA_B3_DESC *data;
-       word ncci;
-
-       if (plci->appl)
-       {
-               TransmitBufferFree(plci->appl, plci->data_sent_ptr);
-               a = plci->adapter;
-               ncci = a->ch_ncci[ch];
-               if (ncci && (a->ncci_plci[ncci] == plci->Id))
-               {
-                       ncci_ptr = &(a->ncci[ncci]);
-                       dbug(1, dprintf("data_out=%d, data_pending=%d", ncci_ptr->data_out, ncci_ptr->data_pending));
-                       if (ncci_ptr->data_pending)
-                       {
-                               data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
-                               if (!(data->Flags & 4) && a->ncci_state[ncci])
-                               {
-                                       Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id;
-                                       if (plci->tel) Id |= EXT_CONTROLLER;
-                                       sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, data->Number,
-                                             "ww", data->Handle, 0);
-                               }
-                               (ncci_ptr->data_out)++;
-                               if (ncci_ptr->data_out == MAX_DATA_B3)
-                                       ncci_ptr->data_out = 0;
-                               (ncci_ptr->data_pending)--;
-                       }
-               }
-       }
-}
-
-static void data_ack(PLCI *plci, byte ch)
-{
-       dword Id;
-       DIVA_CAPI_ADAPTER *a;
-       NCCI *ncci_ptr;
-       word ncci;
-
-       a = plci->adapter;
-       ncci = a->ch_ncci[ch];
-       ncci_ptr = &(a->ncci[ncci]);
-       if (ncci_ptr->data_ack_pending)
-       {
-               if (a->ncci_state[ncci] && (a->ncci_plci[ncci] == plci->Id))
-               {
-                       Id = (((dword)ncci) << 16) | ((word)plci->Id << 8) | a->Id;
-                       if (plci->tel) Id |= EXT_CONTROLLER;
-                       sendf(plci->appl, _DATA_B3_R | CONFIRM, Id, ncci_ptr->DataAck[ncci_ptr->data_ack_out].Number,
-                             "ww", ncci_ptr->DataAck[ncci_ptr->data_ack_out].Handle, 0);
-               }
-               (ncci_ptr->data_ack_out)++;
-               if (ncci_ptr->data_ack_out == MAX_DATA_ACK)
-                       ncci_ptr->data_ack_out = 0;
-               (ncci_ptr->data_ack_pending)--;
-       }
-}
-
-static void sig_ind(PLCI *plci)
-{
-       dword x_Id;
-       dword Id;
-       dword rId;
-       word i;
-       word cip;
-       dword cip_mask;
-       byte *ie;
-       DIVA_CAPI_ADAPTER *a;
-       API_PARSE saved_parms[MAX_MSG_PARMS + 1];
-#define MAXPARMSIDS 31
-       byte *parms[MAXPARMSIDS];
-       byte *add_i[4];
-       byte *multi_fac_parms[MAX_MULTI_IE];
-       byte *multi_pi_parms[MAX_MULTI_IE];
-       byte *multi_ssext_parms[MAX_MULTI_IE];
-       byte *multi_CiPN_parms[MAX_MULTI_IE];
-
-       byte *multi_vswitch_parms[MAX_MULTI_IE];
-
-       byte ai_len;
-       byte *esc_chi = "";
-       byte *esc_law = "";
-       byte *pty_cai = "";
-       byte *esc_cr  = "";
-       byte *esc_profile = "";
-
-       byte facility[256];
-       PLCI *tplci = NULL;
-       byte chi[] = "\x02\x18\x01";
-       byte voice_cai[]  = "\x06\x14\x00\x00\x00\x00\x08";
-       byte resume_cau[] = "\x05\x05\x00\x02\x00\x00";
-       /* ESC_MSGTYPE must be the last but one message, a new IE has to be */
-       /* included before the ESC_MSGTYPE and MAXPARMSIDS has to be incremented */
-       /* SMSG is situated at the end because its 0 (for compatibility reasons */
-       /* (see Info_Mask Bit 4, first IE. then the message type)           */
-       static const word parms_id[] =
-               {MAXPARMSIDS, CPN, 0xff, DSA, OSA, BC, LLC, HLC, ESC_CAUSE, DSP, DT, CHA,
-                UUI, CONG_RR, CONG_RNR, ESC_CHI, KEY, CHI, CAU, ESC_LAW,
-                RDN, RDX, CONN_NR, RIN, NI, CAI, ESC_CR,
-                CST, ESC_PROFILE, 0xff, ESC_MSGTYPE, SMSG};
-       /* 14 FTY repl by ESC_CHI */
-       /* 18 PI  repl by ESC_LAW */
-       /* removed OAD changed to 0xff for future use, OAD is multiIE now */
-       static const word multi_fac_id[] = {1, FTY};
-       static const word multi_pi_id[]  = {1, PI};
-       static const word multi_CiPN_id[]  = {1, OAD};
-       static const word multi_ssext_id[]  = {1, ESC_SSEXT};
-
-       static const word multi_vswitch_id[]  = {1, ESC_VSWITCH};
-
-       byte *cau;
-       word ncci;
-       byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
-       byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00";
-       byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
-       byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\x00\x00\x00\x00";
-       byte force_mt_info = false;
-       byte dir;
-       dword d;
-       word w;
-
-       a = plci->adapter;
-       Id = ((word)plci->Id << 8) | a->Id;
-       PUT_WORD(&SS_Ind[4], 0x0000);
-
-       if (plci->sig_remove_id)
-       {
-               plci->Sig.RNR = 2; /* discard */
-               dbug(1, dprintf("SIG discard while remove pending"));
-               return;
-       }
-       if (plci->tel && plci->SuppState != CALL_HELD) Id |= EXT_CONTROLLER;
-       dbug(1, dprintf("SigInd-Id=%08lx,plci=%x,tel=%x,state=0x%x,channels=%d,Discflowcl=%d",
-                       Id, plci->Id, plci->tel, plci->State, plci->channels, plci->hangup_flow_ctrl_timer));
-       if (plci->Sig.Ind == CALL_HOLD_ACK && plci->channels)
-       {
-               plci->Sig.RNR = 1;
-               return;
-       }
-       if (plci->Sig.Ind == HANGUP && plci->channels)
-       {
-               plci->Sig.RNR = 1;
-               plci->hangup_flow_ctrl_timer++;
-               /* recover the network layer after timeout */
-               if (plci->hangup_flow_ctrl_timer == 100)
-               {
-                       dbug(1, dprintf("Exceptional disc"));
-                       plci->Sig.RNR = 0;
-                       plci->hangup_flow_ctrl_timer = 0;
-                       for (ncci = 1; ncci < MAX_NCCI + 1; ncci++)
-                       {
-                               if (a->ncci_plci[ncci] == plci->Id)
-                               {
-                                       cleanup_ncci_data(plci, ncci);
-                                       if (plci->channels)plci->channels--;
-                                       if (plci->appl)
-                                               sendf(plci->appl, _DISCONNECT_B3_I, (((dword) ncci) << 16) | Id, 0, "ws", 0, "");
-                               }
-                       }
-                       if (plci->appl)
-                               sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
-                       plci_remove(plci);
-                       plci->State = IDLE;
-               }
-               return;
-       }
-
-       /* do first parse the info with no OAD in, because OAD will be converted */
-       /* first the multiple facility IE, then mult. progress ind.              */
-       /* then the parameters for the info_ind + conn_ind                       */
-       IndParse(plci, multi_fac_id, multi_fac_parms, MAX_MULTI_IE);
-       IndParse(plci, multi_pi_id, multi_pi_parms, MAX_MULTI_IE);
-       IndParse(plci, multi_ssext_id, multi_ssext_parms, MAX_MULTI_IE);
-
-       IndParse(plci, multi_vswitch_id, multi_vswitch_parms, MAX_MULTI_IE);
-
-       IndParse(plci, parms_id, parms, 0);
-       IndParse(plci, multi_CiPN_id, multi_CiPN_parms, MAX_MULTI_IE);
-       esc_chi  = parms[14];
-       esc_law  = parms[18];
-       pty_cai  = parms[24];
-       esc_cr   = parms[25];
-       esc_profile = parms[27];
-       if (esc_cr[0] && plci)
-       {
-               if (plci->cr_enquiry && plci->appl)
-               {
-                       plci->cr_enquiry = false;
-                       /* d = MANU_ID            */
-                       /* w = m_command          */
-                       /* b = total length       */
-                       /* b = indication type    */
-                       /* b = length of all IEs  */
-                       /* b = IE1                */
-                       /* S = IE1 length + cont. */
-                       /* b = IE2                */
-                       /* S = IE2 length + cont. */
-                       sendf(plci->appl,
-                             _MANUFACTURER_I,
-                             Id,
-                             0,
-                             "dwbbbbSbS", _DI_MANU_ID, plci->m_command,
-                             2 + 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], plci->Sig.Ind, 1 + 1 + esc_cr[0] + 1 + 1 + esc_law[0], ESC, esc_cr, ESC, esc_law);
-               }
-       }
-       /* create the additional info structure                                  */
-       add_i[1] = parms[15]; /* KEY of additional info */
-       add_i[2] = parms[11]; /* UUI of additional info */
-       ai_len = AddInfo(add_i, multi_fac_parms, esc_chi, facility);
-
-       /* the ESC_LAW indicates if u-Law or a-Law is actually used by the card  */
-       /* indication returns by the card if requested by the function           */
-       /* AutomaticLaw() after driver init                                      */
-       if (a->automatic_law < 4)
-       {
-               if (esc_law[0]) {
-                       if (esc_law[2]) {
-                               dbug(0, dprintf("u-Law selected"));
-                               a->u_law = 1;
-                       }
-                       else {
-                               dbug(0, dprintf("a-Law selected"));
-                               a->u_law = 0;
-                       }
-                       a->automatic_law = 4;
-                       if (plci == a->automatic_lawPLCI) {
-                               plci->internal_command = 0;
-                               sig_req(plci, REMOVE, 0);
-                               send_req(plci);
-                               a->automatic_lawPLCI = NULL;
-                       }
-               }
-               if (esc_profile[0])
-               {
-                       dbug(1, dprintf("[%06x] CardProfile: %lx %lx %lx %lx %lx",
-                                       UnMapController(a->Id), GET_DWORD(&esc_profile[6]),
-                                       GET_DWORD(&esc_profile[10]), GET_DWORD(&esc_profile[14]),
-                                       GET_DWORD(&esc_profile[18]), GET_DWORD(&esc_profile[46])));
-
-                       a->profile.Global_Options &= 0x000000ffL;
-                       a->profile.B1_Protocols &= 0x000003ffL;
-                       a->profile.B2_Protocols &= 0x00001fdfL;
-                       a->profile.B3_Protocols &= 0x000000b7L;
-
-                       a->profile.Global_Options &= GET_DWORD(&esc_profile[6]) |
-                               GL_BCHANNEL_OPERATION_SUPPORTED;
-                       a->profile.B1_Protocols &= GET_DWORD(&esc_profile[10]);
-                       a->profile.B2_Protocols &= GET_DWORD(&esc_profile[14]);
-                       a->profile.B3_Protocols &= GET_DWORD(&esc_profile[18]);
-                       a->manufacturer_features = GET_DWORD(&esc_profile[46]);
-                       a->man_profile.private_options = 0;
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_ECHO_CANCELLER)
-                       {
-                               a->man_profile.private_options |= 1L << PRIVATE_ECHO_CANCELLER;
-                               a->profile.Global_Options |= GL_ECHO_CANCELLER_SUPPORTED;
-                       }
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_RTP)
-                               a->man_profile.private_options |= 1L << PRIVATE_RTP;
-                       a->man_profile.rtp_primary_payloads = GET_DWORD(&esc_profile[50]);
-                       a->man_profile.rtp_additional_payloads = GET_DWORD(&esc_profile[54]);
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_T38)
-                               a->man_profile.private_options |= 1L << PRIVATE_T38;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD)
-                               a->man_profile.private_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_V18)
-                               a->man_profile.private_options |= 1L << PRIVATE_V18;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_TONE)
-                               a->man_profile.private_options |= 1L << PRIVATE_DTMF_TONE;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_PIAFS)
-                               a->man_profile.private_options |= 1L << PRIVATE_PIAFS;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                               a->man_profile.private_options |= 1L << PRIVATE_FAX_PAPER_FORMATS;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_VOWN)
-                               a->man_profile.private_options |= 1L << PRIVATE_VOWN;
-
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_NONSTANDARD)
-                               a->man_profile.private_options |= 1L << PRIVATE_FAX_NONSTANDARD;
-
-               }
-               else
-               {
-                       a->profile.Global_Options &= 0x0000007fL;
-                       a->profile.B1_Protocols &= 0x000003dfL;
-                       a->profile.B2_Protocols &= 0x00001adfL;
-                       a->profile.B3_Protocols &= 0x000000b7L;
-                       a->manufacturer_features &= MANUFACTURER_FEATURE_HARDDTMF;
-               }
-               if (a->manufacturer_features & (MANUFACTURER_FEATURE_HARDDTMF |
-                                               MANUFACTURER_FEATURE_SOFTDTMF_SEND | MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
-               {
-                       a->profile.Global_Options |= GL_DTMF_SUPPORTED;
-               }
-               a->manufacturer_features &= ~MANUFACTURER_FEATURE_OOB_CHANNEL;
-               dbug(1, dprintf("[%06x] Profile: %lx %lx %lx %lx %lx",
-                               UnMapController(a->Id), a->profile.Global_Options,
-                               a->profile.B1_Protocols, a->profile.B2_Protocols,
-                               a->profile.B3_Protocols, a->manufacturer_features));
-       }
-       /* codec plci for the handset/hook state support is just an internal id  */
-       if (plci != a->AdvCodecPLCI)
-       {
-               force_mt_info = SendMultiIE(plci, Id, multi_fac_parms, FTY, 0x20, 0);
-               force_mt_info |= SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, 0);
-               SendSSExtInd(NULL, plci, Id, multi_ssext_parms);
-               SendInfo(plci, Id, parms, force_mt_info);
-
-               VSwitchReqInd(plci, Id, multi_vswitch_parms);
-
-       }
-
-       /* switch the codec to the b-channel                                     */
-       if (esc_chi[0] && plci && !plci->SuppState) {
-               plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
-               mixer_set_bchannel_id_esc(plci, plci->b_channel);
-               dbug(1, dprintf("storeChannel=0x%x", plci->b_channel));
-               if (plci->tel == ADV_VOICE && plci->appl) {
-                       SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
-               }
-       }
-
-       if (plci->appl) plci->appl->Number++;
-
-       switch (plci->Sig.Ind) {
-               /* Response to Get_Supported_Services request */
-       case S_SUPPORTED:
-               dbug(1, dprintf("S_Supported"));
-               if (!plci->appl) break;
-               if (pty_cai[0] == 4)
-               {
-                       PUT_DWORD(&CF_Ind[6], GET_DWORD(&pty_cai[1]));
-               }
-               else
-               {
-                       PUT_DWORD(&CF_Ind[6], MASK_TERMINAL_PORTABILITY | MASK_HOLD_RETRIEVE);
-               }
-               PUT_WORD(&CF_Ind[1], 0);
-               PUT_WORD(&CF_Ind[4], 0);
-               sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0x7, plci->number, "wws", 0, 3, CF_Ind);
-               plci_remove(plci);
-               break;
-
-               /* Supplementary Service rejected */
-       case S_SERVICE_REJ:
-               dbug(1, dprintf("S_Reject=0x%x", pty_cai[5]));
-               if (!pty_cai[0]) break;
-               switch (pty_cai[5])
-               {
-               case ECT_EXECUTE:
-               case THREE_PTY_END:
-               case THREE_PTY_BEGIN:
-                       if (!plci->relatedPTYPLCI) break;
-                       tplci = plci->relatedPTYPLCI;
-                       rId = ((word)tplci->Id << 8) | tplci->adapter->Id;
-                       if (tplci->tel) rId |= EXT_CONTROLLER;
-                       if (pty_cai[5] == ECT_EXECUTE)
-                       {
-                               PUT_WORD(&SS_Ind[1], S_ECT);
-
-                               plci->vswitchstate = 0;
-                               plci->relatedPTYPLCI->vswitchstate = 0;
-
-                       }
-                       else
-                       {
-                               PUT_WORD(&SS_Ind[1], pty_cai[5] + 3);
-                       }
-                       if (pty_cai[2] != 0xff)
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
-                       }
-                       else
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x300E);
-                       }
-                       plci->relatedPTYPLCI = NULL;
-                       plci->ptyState = 0;
-                       sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);
-                       break;
-
-               case CALL_DEFLECTION:
-                       if (pty_cai[2] != 0xff)
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
-                       }
-                       else
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x300E);
-                       }
-                       PUT_WORD(&SS_Ind[1], pty_cai[5]);
-                       for (i = 0; i < max_appl; i++)
-                       {
-                               if (application[i].CDEnable)
-                               {
-                                       if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-                                       application[i].CDEnable = false;
-                               }
-                       }
-                       break;
-
-               case DEACTIVATION_DIVERSION:
-               case ACTIVATION_DIVERSION:
-               case DIVERSION_INTERROGATE_CFU:
-               case DIVERSION_INTERROGATE_CFB:
-               case DIVERSION_INTERROGATE_CFNR:
-               case DIVERSION_INTERROGATE_NUM:
-               case CCBS_REQUEST:
-               case CCBS_DEACTIVATE:
-               case CCBS_INTERROGATE:
-                       if (!plci->appl) break;
-                       if (pty_cai[2] != 0xff)
-                       {
-                               PUT_WORD(&Interr_Err_Ind[4], 0x3600 | (word)pty_cai[2]);
-                       }
-                       else
-                       {
-                               PUT_WORD(&Interr_Err_Ind[4], 0x300E);
-                       }
-                       switch (pty_cai[5])
-                       {
-                       case DEACTIVATION_DIVERSION:
-                               dbug(1, dprintf("Deact_Div"));
-                               Interr_Err_Ind[0] = 0x9;
-                               Interr_Err_Ind[3] = 0x6;
-                               PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_STOP);
-                               break;
-                       case ACTIVATION_DIVERSION:
-                               dbug(1, dprintf("Act_Div"));
-                               Interr_Err_Ind[0] = 0x9;
-                               Interr_Err_Ind[3] = 0x6;
-                               PUT_WORD(&Interr_Err_Ind[1], S_CALL_FORWARDING_START);
-                               break;
-                       case DIVERSION_INTERROGATE_CFU:
-                       case DIVERSION_INTERROGATE_CFB:
-                       case DIVERSION_INTERROGATE_CFNR:
-                               dbug(1, dprintf("Interr_Div"));
-                               Interr_Err_Ind[0] = 0xa;
-                               Interr_Err_Ind[3] = 0x7;
-                               PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_DIVERSION);
-                               break;
-                       case DIVERSION_INTERROGATE_NUM:
-                               dbug(1, dprintf("Interr_Num"));
-                               Interr_Err_Ind[0] = 0xa;
-                               Interr_Err_Ind[3] = 0x7;
-                               PUT_WORD(&Interr_Err_Ind[1], S_INTERROGATE_NUMBERS);
-                               break;
-                       case CCBS_REQUEST:
-                               dbug(1, dprintf("CCBS Request"));
-                               Interr_Err_Ind[0] = 0xd;
-                               Interr_Err_Ind[3] = 0xa;
-                               PUT_WORD(&Interr_Err_Ind[1], S_CCBS_REQUEST);
-                               break;
-                       case CCBS_DEACTIVATE:
-                               dbug(1, dprintf("CCBS Deactivate"));
-                               Interr_Err_Ind[0] = 0x9;
-                               Interr_Err_Ind[3] = 0x6;
-                               PUT_WORD(&Interr_Err_Ind[1], S_CCBS_DEACTIVATE);
-                               break;
-                       case CCBS_INTERROGATE:
-                               dbug(1, dprintf("CCBS Interrogate"));
-                               Interr_Err_Ind[0] = 0xb;
-                               Interr_Err_Ind[3] = 0x8;
-                               PUT_WORD(&Interr_Err_Ind[1], S_CCBS_INTERROGATE);
-                               break;
-                       }
-                       PUT_DWORD(&Interr_Err_Ind[6], plci->appl->S_Handle);
-                       sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, Interr_Err_Ind);
-                       plci_remove(plci);
-                       break;
-               case ACTIVATION_MWI:
-               case DEACTIVATION_MWI:
-                       if (pty_cai[5] == ACTIVATION_MWI)
-                       {
-                               PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE);
-                       }
-                       else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE);
-
-                       if (pty_cai[2] != 0xff)
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x3600 | (word)pty_cai[2]);
-                       }
-                       else
-                       {
-                               PUT_WORD(&SS_Ind[4], 0x300E);
-                       }
-
-                       if (plci->cr_enquiry)
-                       {
-                               sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind);
-                               plci_remove(plci);
-                       }
-                       else
-                       {
-                               sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-                       }
-                       break;
-               case CONF_ADD: /* ERROR */
-               case CONF_BEGIN:
-               case CONF_DROP:
-               case CONF_ISOLATE:
-               case CONF_REATTACH:
-                       CONF_Ind[0] = 9;
-                       CONF_Ind[3] = 6;
-                       switch (pty_cai[5])
-                       {
-                       case CONF_BEGIN:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN);
-                               plci->ptyState = 0;
-                               break;
-                       case CONF_DROP:
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               PUT_WORD(&CONF_Ind[1], S_CONF_DROP);
-                               plci->ptyState = CONNECTED;
-                               break;
-                       case CONF_ISOLATE:
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE);
-                               plci->ptyState = CONNECTED;
-                               break;
-                       case CONF_REATTACH:
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH);
-                               plci->ptyState = CONNECTED;
-                               break;
-                       case CONF_ADD:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_ADD);
-                               plci->relatedPTYPLCI = NULL;
-                               tplci = plci->relatedPTYPLCI;
-                               if (tplci) tplci->ptyState = CONNECTED;
-                               plci->ptyState = CONNECTED;
-                               break;
-                       }
-
-                       if (pty_cai[2] != 0xff)
-                       {
-                               PUT_WORD(&CONF_Ind[4], 0x3600 | (word)pty_cai[2]);
-                       }
-                       else
-                       {
-                               PUT_WORD(&CONF_Ind[4], 0x3303); /* Time-out: network did not respond
-                                                                 within the required time */
-                       }
-
-                       PUT_DWORD(&CONF_Ind[6], 0x0);
-                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
-                       break;
-               }
-               break;
-
-               /* Supplementary Service indicates success */
-       case S_SERVICE:
-               dbug(1, dprintf("Service_Ind"));
-               PUT_WORD(&CF_Ind[4], 0);
-               switch (pty_cai[5])
-               {
-               case THREE_PTY_END:
-               case THREE_PTY_BEGIN:
-               case ECT_EXECUTE:
-                       if (!plci->relatedPTYPLCI) break;
-                       tplci = plci->relatedPTYPLCI;
-                       rId = ((word)tplci->Id << 8) | tplci->adapter->Id;
-                       if (tplci->tel) rId |= EXT_CONTROLLER;
-                       if (pty_cai[5] == ECT_EXECUTE)
-                       {
-                               PUT_WORD(&SS_Ind[1], S_ECT);
-
-                               if (plci->vswitchstate != 3)
-                               {
-
-                                       plci->ptyState = IDLE;
-                                       plci->relatedPTYPLCI = NULL;
-                                       plci->ptyState = 0;
-
-                               }
-
-                               dbug(1, dprintf("ECT OK"));
-                               sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);
-
-
-
-                       }
-                       else
-                       {
-                               switch (plci->ptyState)
-                               {
-                               case S_3PTY_BEGIN:
-                                       plci->ptyState = CONNECTED;
-                                       dbug(1, dprintf("3PTY ON"));
-                                       break;
-
-                               case S_3PTY_END:
-                                       plci->ptyState = IDLE;
-                                       plci->relatedPTYPLCI = NULL;
-                                       plci->ptyState = 0;
-                                       dbug(1, dprintf("3PTY OFF"));
-                                       break;
-                               }
-                               PUT_WORD(&SS_Ind[1], pty_cai[5] + 3);
-                               sendf(tplci->appl, _FACILITY_I, rId, 0, "ws", 3, SS_Ind);
-                       }
-                       break;
-
-               case CALL_DEFLECTION:
-                       PUT_WORD(&SS_Ind[1], pty_cai[5]);
-                       for (i = 0; i < max_appl; i++)
-                       {
-                               if (application[i].CDEnable)
-                               {
-                                       if (application[i].Id) sendf(&application[i], _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-                                       application[i].CDEnable = false;
-                               }
-                       }
-                       break;
-
-               case DEACTIVATION_DIVERSION:
-               case ACTIVATION_DIVERSION:
-                       if (!plci->appl) break;
-                       PUT_WORD(&CF_Ind[1], pty_cai[5] + 2);
-                       PUT_DWORD(&CF_Ind[6], plci->appl->S_Handle);
-                       sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "ws", 3, CF_Ind);
-                       plci_remove(plci);
-                       break;
-
-               case DIVERSION_INTERROGATE_CFU:
-               case DIVERSION_INTERROGATE_CFB:
-               case DIVERSION_INTERROGATE_CFNR:
-               case DIVERSION_INTERROGATE_NUM:
-               case CCBS_REQUEST:
-               case CCBS_DEACTIVATE:
-               case CCBS_INTERROGATE:
-                       if (!plci->appl) break;
-                       switch (pty_cai[5])
-                       {
-                       case DIVERSION_INTERROGATE_CFU:
-                       case DIVERSION_INTERROGATE_CFB:
-                       case DIVERSION_INTERROGATE_CFNR:
-                               dbug(1, dprintf("Interr_Div"));
-                               PUT_WORD(&pty_cai[1], S_INTERROGATE_DIVERSION);
-                               pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
-                               break;
-                       case DIVERSION_INTERROGATE_NUM:
-                               dbug(1, dprintf("Interr_Num"));
-                               PUT_WORD(&pty_cai[1], S_INTERROGATE_NUMBERS);
-                               pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
-                               break;
-                       case CCBS_REQUEST:
-                               dbug(1, dprintf("CCBS Request"));
-                               PUT_WORD(&pty_cai[1], S_CCBS_REQUEST);
-                               pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
-                               break;
-                       case CCBS_DEACTIVATE:
-                               dbug(1, dprintf("CCBS Deactivate"));
-                               PUT_WORD(&pty_cai[1], S_CCBS_DEACTIVATE);
-                               pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
-                               break;
-                       case CCBS_INTERROGATE:
-                               dbug(1, dprintf("CCBS Interrogate"));
-                               PUT_WORD(&pty_cai[1], S_CCBS_INTERROGATE);
-                               pty_cai[3] = pty_cai[0] - 3; /* Supplementary Service-specific parameter len */
-                               break;
-                       }
-                       PUT_WORD(&pty_cai[4], 0); /* Supplementary Service Reason */
-                       PUT_DWORD(&pty_cai[6], plci->appl->S_Handle);
-                       sendf(plci->appl, _FACILITY_I, Id & 0x7, 0, "wS", 3, pty_cai);
-                       plci_remove(plci);
-                       break;
-
-               case ACTIVATION_MWI:
-               case DEACTIVATION_MWI:
-                       if (pty_cai[5] == ACTIVATION_MWI)
-                       {
-                               PUT_WORD(&SS_Ind[1], S_MWI_ACTIVATE);
-                       }
-                       else PUT_WORD(&SS_Ind[1], S_MWI_DEACTIVATE);
-                       if (plci->cr_enquiry)
-                       {
-                               sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "ws", 3, SS_Ind);
-                               plci_remove(plci);
-                       }
-                       else
-                       {
-                               sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-                       }
-                       break;
-               case MWI_INDICATION:
-                       if (pty_cai[0] >= 0x12)
-                       {
-                               PUT_WORD(&pty_cai[3], S_MWI_INDICATE);
-                               pty_cai[2] = pty_cai[0] - 2; /* len Parameter */
-                               pty_cai[5] = pty_cai[0] - 5; /* Supplementary Service-specific parameter len */
-                               if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_MWI))
-                               {
-                                       if (plci->internal_command == GET_MWI_STATE) /* result on Message Waiting Listen */
-                                       {
-                                               sendf(plci->appl, _FACILITY_I, Id & 0xf, 0, "wS", 3, &pty_cai[2]);
-                                               plci_remove(plci);
-                                               return;
-                                       }
-                                       else sendf(plci->appl, _FACILITY_I, Id, 0, "wS", 3, &pty_cai[2]);
-                                       pty_cai[0] = 0;
-                               }
-                               else
-                               {
-                                       for (i = 0; i < max_appl; i++)
-                                       {
-                                               if (a->Notification_Mask[i]&SMASK_MWI)
-                                               {
-                                                       sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "wS", 3, &pty_cai[2]);
-                                                       pty_cai[0] = 0;
-                                               }
-                                       }
-                               }
-
-                               if (!pty_cai[0])
-                               { /* acknowledge */
-                                       facility[2] = 0; /* returncode */
-                               }
-                               else facility[2] = 0xff;
-                       }
-                       else
-                       {
-                               /* reject */
-                               facility[2] = 0xff; /* returncode */
-                       }
-                       facility[0] = 2;
-                       facility[1] = MWI_RESPONSE; /* Function */
-                       add_p(plci, CAI, facility);
-                       add_p(plci, ESC, multi_ssext_parms[0]); /* remembered parameter -> only one possible */
-                       sig_req(plci, S_SERVICE, 0);
-                       send_req(plci);
-                       plci->command = 0;
-                       next_internal_command(Id, plci);
-                       break;
-               case CONF_ADD: /* OK */
-               case CONF_BEGIN:
-               case CONF_DROP:
-               case CONF_ISOLATE:
-               case CONF_REATTACH:
-               case CONF_PARTYDISC:
-                       CONF_Ind[0] = 9;
-                       CONF_Ind[3] = 6;
-                       switch (pty_cai[5])
-                       {
-                       case CONF_BEGIN:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_BEGIN);
-                               if (pty_cai[0] == 6)
-                               {
-                                       d = pty_cai[6];
-                                       PUT_DWORD(&CONF_Ind[6], d); /* PartyID */
-                               }
-                               else
-                               {
-                                       PUT_DWORD(&CONF_Ind[6], 0x0);
-                               }
-                               break;
-                       case CONF_ISOLATE:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_ISOLATE);
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               break;
-                       case CONF_REATTACH:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_REATTACH);
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               break;
-                       case CONF_DROP:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_DROP);
-                               CONF_Ind[0] = 5;
-                               CONF_Ind[3] = 2;
-                               break;
-                       case CONF_ADD:
-                               PUT_WORD(&CONF_Ind[1], S_CONF_ADD);
-                               d = pty_cai[6];
-                               PUT_DWORD(&CONF_Ind[6], d); /* PartyID */
-                               tplci = plci->relatedPTYPLCI;
-                               if (tplci) tplci->ptyState = CONNECTED;
-                               break;
-                       case CONF_PARTYDISC:
-                               CONF_Ind[0] = 7;
-                               CONF_Ind[3] = 4;
-                               PUT_WORD(&CONF_Ind[1], S_CONF_PARTYDISC);
-                               d = pty_cai[6];
-                               PUT_DWORD(&CONF_Ind[4], d); /* PartyID */
-                               break;
-                       }
-                       plci->ptyState = CONNECTED;
-                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
-                       break;
-               case CCBS_INFO_RETAIN:
-               case CCBS_ERASECALLLINKAGEID:
-               case CCBS_STOP_ALERTING:
-                       CONF_Ind[0] = 5;
-                       CONF_Ind[3] = 2;
-                       switch (pty_cai[5])
-                       {
-                       case CCBS_INFO_RETAIN:
-                               PUT_WORD(&CONF_Ind[1], S_CCBS_INFO_RETAIN);
-                               break;
-                       case CCBS_STOP_ALERTING:
-                               PUT_WORD(&CONF_Ind[1], S_CCBS_STOP_ALERTING);
-                               break;
-                       case CCBS_ERASECALLLINKAGEID:
-                               PUT_WORD(&CONF_Ind[1], S_CCBS_ERASECALLLINKAGEID);
-                               CONF_Ind[0] = 7;
-                               CONF_Ind[3] = 4;
-                               CONF_Ind[6] = 0;
-                               CONF_Ind[7] = 0;
-                               break;
-                       }
-                       w = pty_cai[6];
-                       PUT_WORD(&CONF_Ind[4], w); /* PartyID */
-
-                       if (plci->appl && (a->Notification_Mask[plci->appl->Id - 1] & SMASK_CCBS))
-                       {
-                               sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, CONF_Ind);
-                       }
-                       else
-                       {
-                               for (i = 0; i < max_appl; i++)
-                                       if (a->Notification_Mask[i] & SMASK_CCBS)
-                                               sendf(&application[i], _FACILITY_I, Id & 0x7, 0, "ws", 3, CONF_Ind);
-                       }
-                       break;
-               }
-               break;
-       case CALL_HOLD_REJ:
-               cau = parms[7];
-               if (cau)
-               {
-                       i = _L3_CAUSE | cau[2];
-                       if (cau[2] == 0) i = 0x3603;
-               }
-               else
-               {
-                       i = 0x3603;
-               }
-               PUT_WORD(&SS_Ind[1], S_HOLD);
-               PUT_WORD(&SS_Ind[4], i);
-               if (plci->SuppState == HOLD_REQUEST)
-               {
-                       plci->SuppState = IDLE;
-                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-               }
-               break;
-
-       case CALL_HOLD_ACK:
-               if (plci->SuppState == HOLD_REQUEST)
-               {
-                       plci->SuppState = CALL_HELD;
-                       CodecIdCheck(a, plci);
-                       start_internal_command(Id, plci, hold_save_command);
-               }
-               break;
-
-       case CALL_RETRIEVE_REJ:
-               cau = parms[7];
-               if (cau)
-               {
-                       i = _L3_CAUSE | cau[2];
-                       if (cau[2] == 0) i = 0x3603;
-               }
-               else
-               {
-                       i = 0x3603;
-               }
-               PUT_WORD(&SS_Ind[1], S_RETRIEVE);
-               PUT_WORD(&SS_Ind[4], i);
-               if (plci->SuppState == RETRIEVE_REQUEST)
-               {
-                       plci->SuppState = CALL_HELD;
-                       CodecIdCheck(a, plci);
-                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-               }
-               break;
-
-       case CALL_RETRIEVE_ACK:
-               PUT_WORD(&SS_Ind[1], S_RETRIEVE);
-               if (plci->SuppState == RETRIEVE_REQUEST)
-               {
-                       plci->SuppState = IDLE;
-                       plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
-                       plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
-                       if (plci->tel)
-                       {
-                               mixer_set_bchannel_id_esc(plci, plci->b_channel);
-                               dbug(1, dprintf("RetrChannel=0x%x", plci->b_channel));
-                               SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
-                               if (plci->B2_prot == B2_TRANSPARENT && plci->B3_prot == B3_TRANSPARENT)
-                               {
-                                       dbug(1, dprintf("Get B-ch"));
-                                       start_internal_command(Id, plci, retrieve_restore_command);
-                               }
-                               else
-                                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", 3, SS_Ind);
-                       }
-                       else
-                               start_internal_command(Id, plci, retrieve_restore_command);
-               }
-               break;
-
-       case INDICATE_IND:
-               if (plci->State != LISTENING) {
-                       sig_req(plci, HANGUP, 0);
-                       send_req(plci);
-                       break;
-               }
-               cip = find_cip(a, parms[4], parms[6]);
-               cip_mask = 1L << cip;
-               dbug(1, dprintf("cip=%d,cip_mask=%lx", cip, cip_mask));
-               bitmap_zero(plci->c_ind_mask_table, MAX_APPL);
-               if (!remove_started && !a->adapter_disabled)
-               {
-                       group_optimization(a, plci);
-                       for_each_set_bit(i, plci->group_optimization_mask_table, max_appl) {
-                               if (application[i].Id
-                                   && (a->CIP_Mask[i] & 1 || a->CIP_Mask[i] & cip_mask)
-                                   && CPN_filter_ok(parms[0], a, i)) {
-                                       dbug(1, dprintf("storedcip_mask[%d]=0x%lx", i, a->CIP_Mask[i]));
-                                       __set_bit(i, plci->c_ind_mask_table);
-                                       dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table));
-                                       plci->State = INC_CON_PENDING;
-                                       plci->call_dir = (plci->call_dir & ~(CALL_DIR_OUT | CALL_DIR_ORIGINATE)) |
-                                               CALL_DIR_IN | CALL_DIR_ANSWER;
-                                       if (esc_chi[0]) {
-                                               plci->b_channel = esc_chi[esc_chi[0]] & 0x1f;
-                                               mixer_set_bchannel_id_esc(plci, plci->b_channel);
-                                       }
-                                       /* if a listen on the ext controller is done, check if hook states */
-                                       /* are supported or if just a on board codec must be activated     */
-                                       if (a->codec_listen[i] && !a->AdvSignalPLCI) {
-                                               if (a->profile.Global_Options & HANDSET)
-                                                       plci->tel = ADV_VOICE;
-                                               else if (a->profile.Global_Options & ON_BOARD_CODEC)
-                                                       plci->tel = CODEC;
-                                               if (plci->tel) Id |= EXT_CONTROLLER;
-                                               a->codec_listen[i] = plci;
-                                       }
-
-                                       sendf(&application[i], _CONNECT_I, Id, 0,
-                                             "wSSSSSSSbSSSSS", cip,    /* CIP                 */
-                                             parms[0],    /* CalledPartyNumber   */
-                                             multi_CiPN_parms[0],    /* CallingPartyNumber  */
-                                             parms[2],    /* CalledPartySubad    */
-                                             parms[3],    /* CallingPartySubad   */
-                                             parms[4],    /* BearerCapability    */
-                                             parms[5],    /* LowLC               */
-                                             parms[6],    /* HighLC              */
-                                             ai_len,      /* nested struct add_i */
-                                             add_i[0],    /* B channel info    */
-                                             add_i[1],    /* keypad facility   */
-                                             add_i[2],    /* user user data    */
-                                             add_i[3],    /* nested facility   */
-                                             multi_CiPN_parms[1]    /* second CiPN(SCR)   */
-                                               );
-                                       SendSSExtInd(&application[i],
-                                                    plci,
-                                                    Id,
-                                                    multi_ssext_parms);
-                                       SendSetupInfo(&application[i],
-                                                     plci,
-                                                     Id,
-                                                     parms,
-                                                     SendMultiIE(plci, Id, multi_pi_parms, PI, 0x210, true));
-                               }
-                       }
-                       dbug(1, dprintf("c_ind_mask =%*pb", MAX_APPL, plci->c_ind_mask_table));
-               }
-               if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL)) {
-                       sig_req(plci, HANGUP, 0);
-                       send_req(plci);
-                       plci->State = IDLE;
-               }
-               plci->notifiedcall = 0;
-               a->listen_active--;
-               listen_check(a);
-               break;
-
-       case CALL_PEND_NOTIFY:
-               plci->notifiedcall = 1;
-               listen_check(a);
-               break;
-
-       case CALL_IND:
-       case CALL_CON:
-               if (plci->State == ADVANCED_VOICE_SIG || plci->State == ADVANCED_VOICE_NOSIG)
-               {
-                       if (plci->internal_command == PERM_COD_CONN_PEND)
-                       {
-                               if (plci->State == ADVANCED_VOICE_NOSIG)
-                               {
-                                       dbug(1, dprintf("***Codec OK"));
-                                       if (a->AdvSignalPLCI)
-                                       {
-                                               tplci = a->AdvSignalPLCI;
-                                               if (tplci->spoofed_msg)
-                                               {
-                                                       dbug(1, dprintf("***Spoofed Msg(0x%x)", tplci->spoofed_msg));
-                                                       tplci->command = 0;
-                                                       tplci->internal_command = 0;
-                                                       x_Id = ((word)tplci->Id << 8) | tplci->adapter->Id | 0x80;
-                                                       switch (tplci->spoofed_msg)
-                                                       {
-                                                       case CALL_RES:
-                                                               tplci->command = _CONNECT_I | RESPONSE;
-                                                               api_load_msg(&tplci->saved_msg, saved_parms);
-                                                               add_b1(tplci, &saved_parms[1], 0, tplci->B1_facilities);
-                                                               if (tplci->adapter->Info_Mask[tplci->appl->Id - 1] & 0x200)
-                                                               {
-                                                                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                                                                       add_p(tplci, LLI, "\x01\x01");
-                                                               }
-                                                               add_s(tplci, CONN_NR, &saved_parms[2]);
-                                                               add_s(tplci, LLC, &saved_parms[4]);
-                                                               add_ai(tplci, &saved_parms[5]);
-                                                               tplci->State = INC_CON_ACCEPT;
-                                                               sig_req(tplci, CALL_RES, 0);
-                                                               send_req(tplci);
-                                                               break;
-
-                                                       case AWAITING_SELECT_B:
-                                                               dbug(1, dprintf("Select_B continue"));
-                                                               start_internal_command(x_Id, tplci, select_b_command);
-                                                               break;
-
-                                                       case AWAITING_MANUF_CON: /* Get_Plci per Manufacturer_Req to ext controller */
-                                                               if (!tplci->Sig.Id)
-                                                               {
-                                                                       dbug(1, dprintf("No SigID!"));
-                                                                       sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, _OUT_OF_PLCI);
-                                                                       plci_remove(tplci);
-                                                                       break;
-                                                               }
-                                                               tplci->command = _MANUFACTURER_R;
-                                                               api_load_msg(&tplci->saved_msg, saved_parms);
-                                                               dir = saved_parms[2].info[0];
-                                                               if (dir == 1) {
-                                                                       sig_req(tplci, CALL_REQ, 0);
-                                                               }
-                                                               else if (!dir) {
-                                                                       sig_req(tplci, LISTEN_REQ, 0);
-                                                               }
-                                                               send_req(tplci);
-                                                               sendf(tplci->appl, _MANUFACTURER_R | CONFIRM, x_Id, tplci->number, "dww", _DI_MANU_ID, _MANUFACTURER_R, 0);
-                                                               break;
-
-                                                       case (CALL_REQ | AWAITING_MANUF_CON):
-                                                               sig_req(tplci, CALL_REQ, 0);
-                                                               send_req(tplci);
-                                                               break;
-
-                                                       case CALL_REQ:
-                                                               if (!tplci->Sig.Id)
-                                                               {
-                                                                       dbug(1, dprintf("No SigID!"));
-                                                                       sendf(tplci->appl, _CONNECT_R | CONFIRM, tplci->adapter->Id, 0, "w", _OUT_OF_PLCI);
-                                                                       plci_remove(tplci);
-                                                                       break;
-                                                               }
-                                                               tplci->command = _CONNECT_R;
-                                                               api_load_msg(&tplci->saved_msg, saved_parms);
-                                                               add_s(tplci, CPN, &saved_parms[1]);
-                                                               add_s(tplci, DSA, &saved_parms[3]);
-                                                               add_ai(tplci, &saved_parms[9]);
-                                                               sig_req(tplci, CALL_REQ, 0);
-                                                               send_req(tplci);
-                                                               break;
-
-                                                       case CALL_RETRIEVE:
-                                                               tplci->command = C_RETRIEVE_REQ;
-                                                               sig_req(tplci, CALL_RETRIEVE, 0);
-                                                               send_req(tplci);
-                                                               break;
-                                                       }
-                                                       tplci->spoofed_msg = 0;
-                                                       if (tplci->internal_command == 0)
-                                                               next_internal_command(x_Id, tplci);
-                                               }
-                                       }
-                                       next_internal_command(Id, plci);
-                                       break;
-                               }
-                               dbug(1, dprintf("***Codec Hook Init Req"));
-                               plci->internal_command = PERM_COD_HOOK;
-                               add_p(plci, FTY, "\x01\x09");             /* Get Hook State*/
-                               sig_req(plci, TEL_CTRL, 0);
-                               send_req(plci);
-                       }
-               }
-               else if (plci->command != _MANUFACTURER_R  /* old style permanent connect */
-                        && plci->State != INC_ACT_PENDING)
-               {
-                       mixer_set_bchannel_id_esc(plci, plci->b_channel);
-                       if (plci->tel == ADV_VOICE && plci->SuppState == IDLE) /* with permanent codec switch on immediately */
-                       {
-                               chi[2] = plci->b_channel;
-                               SetVoiceChannel(a->AdvCodecPLCI, chi, a);
-                       }
-                       sendf(plci->appl, _CONNECT_ACTIVE_I, Id, 0, "Sss", parms[21], "", "");
-                       plci->State = INC_ACT_PENDING;
-               }
-               break;
-
-       case TEL_CTRL:
-               ie = multi_fac_parms[0]; /* inspect the facility hook indications */
-               if (plci->State == ADVANCED_VOICE_SIG && ie[0]) {
-                       switch (ie[1] & 0x91) {
-                       case 0x80:   /* hook off */
-                       case 0x81:
-                               if (plci->internal_command == PERM_COD_HOOK)
-                               {
-                                       dbug(1, dprintf("init:hook_off"));
-                                       plci->hook_state = ie[1];
-                                       next_internal_command(Id, plci);
-                                       break;
-                               }
-                               else /* ignore doubled hook indications */
-                               {
-                                       if (((plci->hook_state) & 0xf0) == 0x80)
-                                       {
-                                               dbug(1, dprintf("ignore hook"));
-                                               break;
-                                       }
-                                       plci->hook_state = ie[1]&0x91;
-                               }
-                               /* check for incoming call pending */
-                               /* and signal '+'.Appl must decide */
-                               /* with connect_res if call must   */
-                               /* accepted or not                 */
-                               for (i = 0, tplci = NULL; i < max_appl; i++) {
-                                       if (a->codec_listen[i]
-                                           && (a->codec_listen[i]->State == INC_CON_PENDING
-                                               || a->codec_listen[i]->State == INC_CON_ALERT)) {
-                                               tplci = a->codec_listen[i];
-                                               tplci->appl = &application[i];
-                                       }
-                               }
-                               /* no incoming call, do outgoing call */
-                               /* and signal '+' if outg. setup   */
-                               if (!a->AdvSignalPLCI && !tplci) {
-                                       if ((i = get_plci(a))) {
-                                               a->AdvSignalPLCI = &a->plci[i - 1];
-                                               tplci = a->AdvSignalPLCI;
-                                               tplci->tel  = ADV_VOICE;
-                                               PUT_WORD(&voice_cai[5], a->AdvSignalAppl->MaxDataLength);
-                                               if (a->Info_Mask[a->AdvSignalAppl->Id - 1] & 0x200) {
-                                                       /* early B3 connect (CIP mask bit 9) no release after a disc */
-                                                       add_p(tplci, LLI, "\x01\x01");
-                                               }
-                                               add_p(tplci, CAI, voice_cai);
-                                               add_p(tplci, OAD, a->TelOAD);
-                                               add_p(tplci, OSA, a->TelOSA);
-                                               add_p(tplci, SHIFT | 6, NULL);
-                                               add_p(tplci, SIN, "\x02\x01\x00");
-                                               add_p(tplci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               sig_req(tplci, ASSIGN, DSIG_ID);
-                                               a->AdvSignalPLCI->internal_command = HOOK_OFF_REQ;
-                                               a->AdvSignalPLCI->command = 0;
-                                               tplci->appl = a->AdvSignalAppl;
-                                               tplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-                                               send_req(tplci);
-                                       }
-
-                               }
-
-                               if (!tplci) break;
-                               Id = ((word)tplci->Id << 8) | a->Id;
-                               Id |= EXT_CONTROLLER;
-                               sendf(tplci->appl,
-                                     _FACILITY_I,
-                                     Id,
-                                     0,
-                                     "ws", (word)0, "\x01+");
-                               break;
-
-                       case 0x90:   /* hook on  */
-                       case 0x91:
-                               if (plci->internal_command == PERM_COD_HOOK)
-                               {
-                                       dbug(1, dprintf("init:hook_on"));
-                                       plci->hook_state = ie[1] & 0x91;
-                                       next_internal_command(Id, plci);
-                                       break;
-                               }
-                               else /* ignore doubled hook indications */
-                               {
-                                       if (((plci->hook_state) & 0xf0) == 0x90) break;
-                                       plci->hook_state = ie[1] & 0x91;
-                               }
-                               /* hangup the adv. voice call and signal '-' to the appl */
-                               if (a->AdvSignalPLCI) {
-                                       Id = ((word)a->AdvSignalPLCI->Id << 8) | a->Id;
-                                       if (plci->tel) Id |= EXT_CONTROLLER;
-                                       sendf(a->AdvSignalAppl,
-                                             _FACILITY_I,
-                                             Id,
-                                             0,
-                                             "ws", (word)0, "\x01-");
-                                       a->AdvSignalPLCI->internal_command = HOOK_ON_REQ;
-                                       a->AdvSignalPLCI->command = 0;
-                                       sig_req(a->AdvSignalPLCI, HANGUP, 0);
-                                       send_req(a->AdvSignalPLCI);
-                               }
-                               break;
-                       }
-               }
-               break;
-
-       case RESUME:
-               __clear_bit(plci->appl->Id - 1, plci->c_ind_mask_table);
-               PUT_WORD(&resume_cau[4], GOOD);
-               sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau);
-               break;
-
-       case SUSPEND:
-               bitmap_zero(plci->c_ind_mask_table, MAX_APPL);
-
-               if (plci->NL.Id && !plci->nl_remove_id) {
-                       mixer_remove(plci);
-                       nl_req_ncci(plci, REMOVE, 0);
-               }
-               if (!plci->sig_remove_id) {
-                       plci->internal_command = 0;
-                       sig_req(plci, REMOVE, 0);
-               }
-               send_req(plci);
-               if (!plci->channels) {
-                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, "\x05\x04\x00\x02\x00\x00");
-                       sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
-               }
-               break;
-
-       case SUSPEND_REJ:
-               break;
-
-       case HANGUP:
-               plci->hangup_flow_ctrl_timer = 0;
-               if (plci->manufacturer && plci->State == LOCAL_CONNECT) break;
-               cau = parms[7];
-               if (cau) {
-                       i = _L3_CAUSE | cau[2];
-                       if (cau[2] == 0) i = 0;
-                       else if (cau[2] == 8) i = _L1_ERROR;
-                       else if (cau[2] == 9 || cau[2] == 10) i = _L2_ERROR;
-                       else if (cau[2] == 5) i = _CAPI_GUARD_ERROR;
-               }
-               else {
-                       i = _L3_ERROR;
-               }
-
-               if (plci->State == INC_CON_PENDING || plci->State == INC_CON_ALERT)
-               {
-                       for_each_set_bit(i, plci->c_ind_mask_table, max_appl)
-                               sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
-               }
-               else
-               {
-                       bitmap_zero(plci->c_ind_mask_table, MAX_APPL);
-               }
-               if (!plci->appl)
-               {
-                       if (plci->State == LISTENING)
-                       {
-                               plci->notifiedcall = 0;
-                               a->listen_active--;
-                       }
-                       plci->State = INC_DIS_PENDING;
-                       if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL))
-                       {
-                               plci->State = IDLE;
-                               if (plci->NL.Id && !plci->nl_remove_id)
-                               {
-                                       mixer_remove(plci);
-                                       nl_req_ncci(plci, REMOVE, 0);
-                               }
-                               if (!plci->sig_remove_id)
-                               {
-                                       plci->internal_command = 0;
-                                       sig_req(plci, REMOVE, 0);
-                               }
-                               send_req(plci);
-                       }
-               }
-               else
-               {
-                       /* collision of DISCONNECT or CONNECT_RES with HANGUP can   */
-                       /* result in a second HANGUP! Don't generate another        */
-                       /* DISCONNECT                                               */
-                       if (plci->State != IDLE && plci->State != INC_DIS_PENDING)
-                       {
-                               if (plci->State == RESUMING)
-                               {
-                                       PUT_WORD(&resume_cau[4], i);
-                                       sendf(plci->appl, _FACILITY_I, Id, 0, "ws", (word)3, resume_cau);
-                               }
-                               plci->State = INC_DIS_PENDING;
-                               sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", i);
-                       }
-               }
-               break;
-
-       case SSEXT_IND:
-               SendSSExtInd(NULL, plci, Id, multi_ssext_parms);
-               break;
-
-       case VSWITCH_REQ:
-               VSwitchReqInd(plci, Id, multi_vswitch_parms);
-               break;
-       case VSWITCH_IND:
-               if (plci->relatedPTYPLCI &&
-                   plci->vswitchstate == 3 &&
-                   plci->relatedPTYPLCI->vswitchstate == 3 &&
-                   parms[MAXPARMSIDS - 1][0])
-               {
-                       add_p(plci->relatedPTYPLCI, SMSG, parms[MAXPARMSIDS - 1]);
-                       sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
-                       send_req(plci->relatedPTYPLCI);
-               }
-               else VSwitchReqInd(plci, Id, multi_vswitch_parms);
-               break;
-
-       }
-}
-
-
-static void SendSetupInfo(APPL *appl, PLCI *plci, dword Id, byte **parms, byte Info_Sent_Flag)
-{
-       word i;
-       byte *ie;
-       word Info_Number;
-       byte *Info_Element;
-       word Info_Mask = 0;
-
-       dbug(1, dprintf("SetupInfo"));
-
-       for (i = 0; i < MAXPARMSIDS; i++) {
-               ie = parms[i];
-               Info_Number = 0;
-               Info_Element = ie;
-               if (ie[0]) {
-                       switch (i) {
-                       case 0:
-                               dbug(1, dprintf("CPN "));
-                               Info_Number = 0x0070;
-                               Info_Mask = 0x80;
-                               Info_Sent_Flag = true;
-                               break;
-                       case 8:  /* display      */
-                               dbug(1, dprintf("display(%d)", i));
-                               Info_Number = 0x0028;
-                               Info_Mask = 0x04;
-                               Info_Sent_Flag = true;
-                               break;
-                       case 16: /* Channel Id */
-                               dbug(1, dprintf("CHI"));
-                               Info_Number = 0x0018;
-                               Info_Mask = 0x100;
-                               Info_Sent_Flag = true;
-                               mixer_set_bchannel_id(plci, Info_Element);
-                               break;
-                       case 19: /* Redirected Number */
-                               dbug(1, dprintf("RDN"));
-                               Info_Number = 0x0074;
-                               Info_Mask = 0x400;
-                               Info_Sent_Flag = true;
-                               break;
-                       case 20: /* Redirected Number extended */
-                               dbug(1, dprintf("RDX"));
-                               Info_Number = 0x0073;
-                               Info_Mask = 0x400;
-                               Info_Sent_Flag = true;
-                               break;
-                       case 22: /* Redirecing Number  */
-                               dbug(1, dprintf("RIN"));
-                               Info_Number = 0x0076;
-                               Info_Mask = 0x400;
-                               Info_Sent_Flag = true;
-                               break;
-                       default:
-                               Info_Number = 0;
-                               break;
-                       }
-               }
-
-               if (i == MAXPARMSIDS - 2) { /* to indicate the message type "Setup" */
-                       Info_Number = 0x8000 | 5;
-                       Info_Mask = 0x10;
-                       Info_Element = "";
-               }
-
-               if (Info_Sent_Flag && Info_Number) {
-                       if (plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask) {
-                               sendf(appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
-                       }
-               }
-       }
-}
-
-
-static void SendInfo(PLCI *plci, dword Id, byte **parms, byte iesent)
-{
-       word i;
-       word j;
-       word k;
-       byte *ie;
-       word Info_Number;
-       byte *Info_Element;
-       word Info_Mask = 0;
-       static byte charges[5] = {4, 0, 0, 0, 0};
-       static byte cause[] = {0x02, 0x80, 0x00};
-       APPL *appl;
-
-       dbug(1, dprintf("InfoParse "));
-
-       if (
-               !plci->appl
-               && !plci->State
-               && plci->Sig.Ind != NCR_FACILITY
-               )
-       {
-               dbug(1, dprintf("NoParse "));
-               return;
-       }
-       cause[2] = 0;
-       for (i = 0; i < MAXPARMSIDS; i++) {
-               ie = parms[i];
-               Info_Number = 0;
-               Info_Element = ie;
-               if (ie[0]) {
-                       switch (i) {
-                       case 0:
-                               dbug(1, dprintf("CPN "));
-                               Info_Number = 0x0070;
-                               Info_Mask   = 0x80;
-                               break;
-                       case 7: /* ESC_CAU */
-                               dbug(1, dprintf("cau(0x%x)", ie[2]));
-                               Info_Number = 0x0008;
-                               Info_Mask = 0x00;
-                               cause[2] = ie[2];
-                               Info_Element = NULL;
-                               break;
-                       case 8:  /* display      */
-                               dbug(1, dprintf("display(%d)", i));
-                               Info_Number = 0x0028;
-                               Info_Mask = 0x04;
-                               break;
-                       case 9:  /* Date display */
-                               dbug(1, dprintf("date(%d)", i));
-                               Info_Number = 0x0029;
-                               Info_Mask = 0x02;
-                               break;
-                       case 10: /* charges */
-                               for (j = 0; j < 4; j++) charges[1 + j] = 0;
-                               for (j = 0; j < ie[0] && !(ie[1 + j] & 0x80); j++);
-                               for (k = 1, j++; j < ie[0] && k <= 4; j++, k++) charges[k] = ie[1 + j];
-                               Info_Number = 0x4000;
-                               Info_Mask = 0x40;
-                               Info_Element = charges;
-                               break;
-                       case 11: /* user user info */
-                               dbug(1, dprintf("uui"));
-                               Info_Number = 0x007E;
-                               Info_Mask = 0x08;
-                               break;
-                       case 12: /* congestion receiver ready */
-                               dbug(1, dprintf("clRDY"));
-                               Info_Number = 0x00B0;
-                               Info_Mask = 0x08;
-                               Info_Element = "";
-                               break;
-                       case 13: /* congestion receiver not ready */
-                               dbug(1, dprintf("clNRDY"));
-                               Info_Number = 0x00BF;
-                               Info_Mask = 0x08;
-                               Info_Element = "";
-                               break;
-                       case 15: /* Keypad Facility */
-                               dbug(1, dprintf("KEY"));
-                               Info_Number = 0x002C;
-                               Info_Mask = 0x20;
-                               break;
-                       case 16: /* Channel Id */
-                               dbug(1, dprintf("CHI"));
-                               Info_Number = 0x0018;
-                               Info_Mask = 0x100;
-                               mixer_set_bchannel_id(plci, Info_Element);
-                               break;
-                       case 17: /* if no 1tr6 cause, send full cause, else esc_cause */
-                               dbug(1, dprintf("q9cau(0x%x)", ie[2]));
-                               if (!cause[2] || cause[2] < 0x80) break;  /* eg. layer 1 error */
-                               Info_Number = 0x0008;
-                               Info_Mask = 0x01;
-                               if (cause[2] != ie[2]) Info_Element = cause;
-                               break;
-                       case 19: /* Redirected Number */
-                               dbug(1, dprintf("RDN"));
-                               Info_Number = 0x0074;
-                               Info_Mask = 0x400;
-                               break;
-                       case 22: /* Redirecing Number  */
-                               dbug(1, dprintf("RIN"));
-                               Info_Number = 0x0076;
-                               Info_Mask = 0x400;
-                               break;
-                       case 23: /* Notification Indicator  */
-                               dbug(1, dprintf("NI"));
-                               Info_Number = (word)NI;
-                               Info_Mask = 0x210;
-                               break;
-                       case 26: /* Call State  */
-                               dbug(1, dprintf("CST"));
-                               Info_Number = (word)CST;
-                               Info_Mask = 0x01; /* do with cause i.e. for now */
-                               break;
-                       case MAXPARMSIDS - 2:  /* Escape Message Type, must be the last indication */
-                               dbug(1, dprintf("ESC/MT[0x%x]", ie[3]));
-                               Info_Number = 0x8000 | ie[3];
-                               if (iesent) Info_Mask = 0xffff;
-                               else  Info_Mask = 0x10;
-                               Info_Element = "";
-                               break;
-                       default:
-                               Info_Number  = 0;
-                               Info_Mask    = 0;
-                               Info_Element = "";
-                               break;
-                       }
-               }
-
-               if (plci->Sig.Ind == NCR_FACILITY)           /* check controller broadcast */
-               {
-                       for (j = 0; j < max_appl; j++)
-                       {
-                               appl = &application[j];
-                               if (Info_Number
-                                   && appl->Id
-                                   && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask)
-                               {
-                                       dbug(1, dprintf("NCR_Ind"));
-                                       iesent = true;
-                                       sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element);
-                               }
-                       }
-               }
-               else if (!plci->appl)
-               { /* overlap receiving broadcast */
-                       if (Info_Number == CPN
-                           || Info_Number == KEY
-                           || Info_Number == NI
-                           || Info_Number == DSP
-                           || Info_Number == UUI)
-                       {
-                               for_each_set_bit(j, plci->c_ind_mask_table, max_appl) {
-                                       dbug(1, dprintf("Ovl_Ind"));
-                                       iesent = true;
-                                       sendf(&application[j], _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
-                               }
-                       }
-               }               /* all other signalling states */
-               else if (Info_Number
-                        && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask)
-               {
-                       dbug(1, dprintf("Std_Ind"));
-                       iesent = true;
-                       sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
-               }
-       }
-}
-
-
-static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type,
-                       dword info_mask, byte setupParse)
-{
-       word i;
-       word j;
-       byte *ie;
-       word Info_Number;
-       byte *Info_Element;
-       APPL *appl;
-       word Info_Mask = 0;
-       byte iesent = 0;
-
-       if (
-               !plci->appl
-               && !plci->State
-               && plci->Sig.Ind != NCR_FACILITY
-               && !setupParse
-               )
-       {
-               dbug(1, dprintf("NoM-IEParse "));
-               return 0;
-       }
-       dbug(1, dprintf("M-IEParse "));
-
-       for (i = 0; i < MAX_MULTI_IE; i++)
-       {
-               ie = parms[i];
-               Info_Number = 0;
-               Info_Element = ie;
-               if (ie[0])
-               {
-                       dbug(1, dprintf("[Ind0x%x]:IE=0x%x", plci->Sig.Ind, ie_type));
-                       Info_Number = (word)ie_type;
-                       Info_Mask = (word)info_mask;
-               }
-
-               if (plci->Sig.Ind == NCR_FACILITY)           /* check controller broadcast */
-               {
-                       for (j = 0; j < max_appl; j++)
-                       {
-                               appl = &application[j];
-                               if (Info_Number
-                                   && appl->Id
-                                   && plci->adapter->Info_Mask[appl->Id - 1] & Info_Mask)
-                               {
-                                       iesent = true;
-                                       dbug(1, dprintf("Mlt_NCR_Ind"));
-                                       sendf(&application[j], _INFO_I, Id & 0x0f, 0, "wS", Info_Number, Info_Element);
-                               }
-                       }
-               }
-               else if (!plci->appl && Info_Number)
-               {                                        /* overlap receiving broadcast */
-                       for_each_set_bit(j, plci->c_ind_mask_table, max_appl) {
-                               iesent = true;
-                               dbug(1, dprintf("Mlt_Ovl_Ind"));
-                               sendf(&application[j] , _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
-                       }
-               }                                        /* all other signalling states */
-               else if (Info_Number
-                        && plci->adapter->Info_Mask[plci->appl->Id - 1] & Info_Mask)
-               {
-                       iesent = true;
-                       dbug(1, dprintf("Mlt_Std_Ind"));
-                       sendf(plci->appl, _INFO_I, Id, 0, "wS", Info_Number, Info_Element);
-               }
-       }
-       return iesent;
-}
-
-static void SendSSExtInd(APPL *appl, PLCI *plci, dword Id, byte **parms)
-{
-       word i;
-       /* Format of multi_ssext_parms[i][]:
-          0 byte length
-          1 byte SSEXTIE
-          2 byte SSEXT_REQ/SSEXT_IND
-          3 byte length
-          4 word SSExtCommand
-          6... Params
-       */
-       if (
-               plci
-               && plci->State
-               && plci->Sig.Ind != NCR_FACILITY
-               )
-               for (i = 0; i < MAX_MULTI_IE; i++)
-               {
-                       if (parms[i][0] < 6) continue;
-                       if (parms[i][2] == SSEXT_REQ) continue;
-
-                       if (appl)
-                       {
-                               parms[i][0] = 0; /* kill it */
-                               sendf(appl, _MANUFACTURER_I,
-                                     Id,
-                                     0,
-                                     "dwS",
-                                     _DI_MANU_ID,
-                                     _DI_SSEXT_CTRL,
-                                     &parms[i][3]);
-                       }
-                       else if (plci->appl)
-                       {
-                               parms[i][0] = 0; /* kill it */
-                               sendf(plci->appl, _MANUFACTURER_I,
-                                     Id,
-                                     0,
-                                     "dwS",
-                                     _DI_MANU_ID,
-                                     _DI_SSEXT_CTRL,
-                                     &parms[i][3]);
-                       }
-               }
-};
-
-static void nl_ind(PLCI *plci)
-{
-       byte ch;
-       word ncci;
-       dword Id;
-       DIVA_CAPI_ADAPTER *a;
-       word NCCIcode;
-       APPL *APPLptr;
-       word count;
-       word Num;
-       word i, ncpi_state;
-       byte len, ncci_state;
-       word msg;
-       word info = 0;
-       word fax_feature_bits;
-       byte fax_send_edata_ack;
-       static byte v120_header_buffer[2 + 3];
-       static word fax_info[] = {
-               0,                     /* T30_SUCCESS                        */
-               _FAX_NO_CONNECTION,    /* T30_ERR_NO_DIS_RECEIVED            */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_RESPONSE        */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_RESPONSE          */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_TOO_MANY_REPEATS           */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_UNEXPECTED_MESSAGE         */
-               _FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DCN             */
-               _FAX_LOCAL_ABORT,      /* T30_ERR_DTC_UNSUPPORTED            */
-               _FAX_TRAINING_ERROR,   /* T30_ERR_ALL_RATES_FAILED           */
-               _FAX_TRAINING_ERROR,   /* T30_ERR_TOO_MANY_TRAINS            */
-               _FAX_PARAMETER_ERROR,  /* T30_ERR_RECEIVE_CORRUPTED          */
-               _FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DISC            */
-               _FAX_LOCAL_ABORT,      /* T30_ERR_APPLICATION_DISC           */
-               _FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_DIS           */
-               _FAX_LOCAL_ABORT,      /* T30_ERR_INCOMPATIBLE_DCS           */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_COMMAND         */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_COMMAND           */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_COMMAND_TOO_LONG   */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_RESPONSE_TOO_LONG  */
-               _FAX_NO_CONNECTION,    /* T30_ERR_NOT_IDENTIFIED             */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_SUPERVISORY_TIMEOUT        */
-               _FAX_PARAMETER_ERROR,  /* T30_ERR_TOO_LONG_SCAN_LINE         */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_MPS    */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_CFR    */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_FTT     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_EOM     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_MPS     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_MCF     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_RTN     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_CFR               */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOP     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOM     */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_MPS     */
-               0x331d,                /* T30_ERR_SUB_SEP_UNSUPPORTED        */
-               0x331e,                /* T30_ERR_PWD_UNSUPPORTED            */
-               0x331f,                /* T30_ERR_SUB_SEP_PWD_UNSUPPORTED    */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_INVALID_COMMAND_FRAME      */
-               _FAX_PARAMETER_ERROR,  /* T30_ERR_UNSUPPORTED_PAGE_CODING    */
-               _FAX_PARAMETER_ERROR,  /* T30_ERR_INVALID_PAGE_CODING        */
-               _FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_PAGE_CONFIG   */
-               _FAX_LOCAL_ABORT,      /* T30_ERR_TIMEOUT_FROM_APPLICATION   */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_NO_REACTION_ON_MARK */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_TRAINING_TIMEOUT    */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_UNEXPECTED_V21      */
-               _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_PRIMARY_CTS_ON      */
-               _FAX_LOCAL_ABORT,      /* T30_ERR_V34FAX_TURNAROUND_POLLING  */
-               _FAX_LOCAL_ABORT       /* T30_ERR_V34FAX_V8_INCOMPATIBILITY  */
-       };
-
-       byte dtmf_code_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE + 1];
-
-
-       static word rtp_info[] = {
-               GOOD,                  /* RTP_SUCCESS                       */
-               0x3600                 /* RTP_ERR_SSRC_OR_PAYLOAD_CHANGE    */
-       };
-
-       static dword udata_forwarding_table[0x100 / sizeof(dword)] =
-               {
-                       0x0020301e, 0x00000000, 0x00000000, 0x00000000,
-                       0x00000000, 0x00000000, 0x00000000, 0x00000000
-               };
-
-       ch = plci->NL.IndCh;
-       a = plci->adapter;
-       ncci = a->ch_ncci[ch];
-       Id = (((dword)(ncci ? ncci : ch)) << 16) | (((word) plci->Id) << 8) | a->Id;
-       if (plci->tel) Id |= EXT_CONTROLLER;
-       APPLptr = plci->appl;
-       dbug(1, dprintf("NL_IND-Id(NL:0x%x)=0x%08lx,plci=%x,tel=%x,state=0x%x,ch=0x%x,chs=%d,Ind=%x",
-                       plci->NL.Id, Id, plci->Id, plci->tel, plci->State, ch, plci->channels, plci->NL.Ind & 0x0f));
-
-       /* in the case if no connect_active_Ind was sent to the appl we wait for */
-
-       if (plci->nl_remove_id)
-       {
-               plci->NL.RNR = 2; /* discard */
-               dbug(1, dprintf("NL discard while remove pending"));
-               return;
-       }
-       if ((plci->NL.Ind & 0x0f) == N_CONNECT)
-       {
-               if (plci->State == INC_DIS_PENDING
-                   || plci->State == OUTG_DIS_PENDING
-                   || plci->State == IDLE)
-               {
-                       plci->NL.RNR = 2; /* discard */
-                       dbug(1, dprintf("discard n_connect"));
-                       return;
-               }
-               if (plci->State < INC_ACT_PENDING)
-               {
-                       plci->NL.RNR = 1; /* flow control */
-                       channel_x_off(plci, ch, N_XON_CONNECT_IND);
-                       return;
-               }
-       }
-
-       if (!APPLptr)                         /* no application or invalid data */
-       {                                    /* while reloading the DSP        */
-               dbug(1, dprintf("discard1"));
-               plci->NL.RNR = 2;
-               return;
-       }
-
-       if (((plci->NL.Ind & 0x0f) == N_UDATA)
-           && (((plci->B2_prot != B2_SDLC) && ((plci->B1_resource == 17) || (plci->B1_resource == 18)))
-               || (plci->B2_prot == 7)
-               || (plci->B3_prot == 7)))
-       {
-               plci->ncpi_buffer[0] = 0;
-
-               ncpi_state = plci->ncpi_state;
-               if (plci->NL.complete == 1)
-               {
-                       byte *data = &plci->NL.RBuffer->P[0];
-
-                       if ((plci->NL.RBuffer->length >= 12)
-                           && ((*data == DSP_UDATA_INDICATION_DCD_ON)
-                               || (*data == DSP_UDATA_INDICATION_CTS_ON)))
-                       {
-                               word conn_opt, ncpi_opt = 0x00;
-/*      HexDump ("MDM N_UDATA:", plci->NL.RBuffer->length, data); */
-
-                               if (*data == DSP_UDATA_INDICATION_DCD_ON)
-                                       plci->ncpi_state |= NCPI_MDM_DCD_ON_RECEIVED;
-                               if (*data == DSP_UDATA_INDICATION_CTS_ON)
-                                       plci->ncpi_state |= NCPI_MDM_CTS_ON_RECEIVED;
-
-                               data++;    /* indication code */
-                               data += 2; /* timestamp */
-                               if ((*data == DSP_CONNECTED_NORM_V18) || (*data == DSP_CONNECTED_NORM_VOWN))
-                                       ncpi_state &= ~(NCPI_MDM_DCD_ON_RECEIVED | NCPI_MDM_CTS_ON_RECEIVED);
-                               data++;    /* connected norm */
-                               conn_opt = GET_WORD(data);
-                               data += 2; /* connected options */
-
-                               PUT_WORD(&(plci->ncpi_buffer[1]), (word)(GET_DWORD(data) & 0x0000FFFF));
-
-                               if (conn_opt & DSP_CONNECTED_OPTION_MASK_V42)
-                               {
-                                       ncpi_opt |= MDM_NCPI_ECM_V42;
-                               }
-                               else if (conn_opt & DSP_CONNECTED_OPTION_MASK_MNP)
-                               {
-                                       ncpi_opt |= MDM_NCPI_ECM_MNP;
-                               }
-                               else
-                               {
-                                       ncpi_opt |= MDM_NCPI_TRANSPARENT;
-                               }
-                               if (conn_opt & DSP_CONNECTED_OPTION_MASK_COMPRESSION)
-                               {
-                                       ncpi_opt |= MDM_NCPI_COMPRESSED;
-                               }
-                               PUT_WORD(&(plci->ncpi_buffer[3]), ncpi_opt);
-                               plci->ncpi_buffer[0] = 4;
-
-                               plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND | NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
-                       }
-               }
-               if (plci->B3_prot == 7)
-               {
-                       if (((a->ncci_state[ncci] == INC_ACT_PENDING) || (a->ncci_state[ncci] == OUTG_CON_PENDING))
-                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                       {
-                               a->ncci_state[ncci] = INC_ACT_PENDING;
-                               sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-                               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                       }
-               }
-
-               if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                     & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
-                   || !(ncpi_state & NCPI_MDM_DCD_ON_RECEIVED)
-                   || !(ncpi_state & NCPI_MDM_CTS_ON_RECEIVED))
-
-               {
-                       plci->NL.RNR = 2;
-                       return;
-               }
-       }
-
-       if (plci->NL.complete == 2)
-       {
-               if (((plci->NL.Ind & 0x0f) == N_UDATA)
-                   && !(udata_forwarding_table[plci->RData[0].P[0] >> 5] & (1L << (plci->RData[0].P[0] & 0x1f))))
-               {
-                       switch (plci->RData[0].P[0])
-                       {
-
-                       case DTMF_UDATA_INDICATION_FAX_CALLING_TONE:
-                               if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
-                                       sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01X");
-                               break;
-                       case DTMF_UDATA_INDICATION_ANSWER_TONE:
-                               if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
-                                       sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", SELECTOR_DTMF, "\x01Y");
-                               break;
-                       case DTMF_UDATA_INDICATION_DIGITS_RECEIVED:
-                               dtmf_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
-                               break;
-                       case DTMF_UDATA_INDICATION_DIGITS_SENT:
-                               dtmf_confirmation(Id, plci);
-                               break;
-
-
-                       case UDATA_INDICATION_MIXER_TAP_DATA:
-                               capidtmf_recv_process_block(&(plci->capidtmf_state), plci->RData[0].P + 1, (word)(plci->RData[0].PLength - 1));
-                               i = capidtmf_indication(&(plci->capidtmf_state), dtmf_code_buffer + 1);
-                               if (i != 0)
-                               {
-                                       dtmf_code_buffer[0] = DTMF_UDATA_INDICATION_DIGITS_RECEIVED;
-                                       dtmf_indication(Id, plci, dtmf_code_buffer, (word)(i + 1));
-                               }
-                               break;
-
-
-                       case UDATA_INDICATION_MIXER_COEFS_SET:
-                               mixer_indication_coefs_set(Id, plci);
-                               break;
-                       case UDATA_INDICATION_XCONNECT_FROM:
-                               mixer_indication_xconnect_from(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
-                               break;
-                       case UDATA_INDICATION_XCONNECT_TO:
-                               mixer_indication_xconnect_to(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
-                               break;
-
-
-                       case LEC_UDATA_INDICATION_DISABLE_DETECT:
-                               ec_indication(Id, plci, plci->RData[0].P, plci->RData[0].PLength);
-                               break;
-
-
-
-                       default:
-                               break;
-                       }
-               }
-               else
-               {
-                       if ((plci->RData[0].PLength != 0)
-                           && ((plci->B2_prot == B2_V120_ASYNC)
-                               || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
-                               || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
-                       {
-
-                               sendf(plci->appl, _DATA_B3_I, Id, 0,
-                                     "dwww",
-                                     plci->RData[1].P,
-                                     (plci->NL.RNum < 2) ? 0 : plci->RData[1].PLength,
-                                     plci->RNum,
-                                     plci->RFlags);
-
-                       }
-                       else
-                       {
-
-                               sendf(plci->appl, _DATA_B3_I, Id, 0,
-                                     "dwww",
-                                     plci->RData[0].P,
-                                     plci->RData[0].PLength,
-                                     plci->RNum,
-                                     plci->RFlags);
-
-                       }
-               }
-               return;
-       }
-
-       fax_feature_bits = 0;
-       if ((plci->NL.Ind & 0x0f) == N_CONNECT ||
-           (plci->NL.Ind & 0x0f) == N_CONNECT_ACK ||
-           (plci->NL.Ind & 0x0f) == N_DISC ||
-           (plci->NL.Ind & 0x0f) == N_EDATA ||
-           (plci->NL.Ind & 0x0f) == N_DISC_ACK)
-       {
-               info = 0;
-               plci->ncpi_buffer[0] = 0;
-               switch (plci->B3_prot) {
-               case  0: /*XPARENT*/
-               case  1: /*T.90 NL*/
-                       break;    /* no network control protocol info - jfr */
-               case  2: /*ISO8202*/
-               case  3: /*X25 DCE*/
-                       for (i = 0; i < plci->NL.RLength; i++) plci->ncpi_buffer[4 + i] = plci->NL.RBuffer->P[i];
-                       plci->ncpi_buffer[0] = (byte)(i + 3);
-                       plci->ncpi_buffer[1] = (byte)(plci->NL.Ind & N_D_BIT ? 1 : 0);
-                       plci->ncpi_buffer[2] = 0;
-                       plci->ncpi_buffer[3] = 0;
-                       break;
-               case  4: /*T.30 - FAX*/
-               case  5: /*T.30 - FAX*/
-                       if (plci->NL.RLength >= sizeof(T30_INFO))
-                       {
-                               dbug(1, dprintf("FaxStatus %04x", ((T30_INFO *)plci->NL.RBuffer->P)->code));
-                               len = 9;
-                               PUT_WORD(&(plci->ncpi_buffer[1]), ((T30_INFO *)plci->NL.RBuffer->P)->rate_div_2400 * 2400);
-                               fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
-                               i = (((T30_INFO *)plci->NL.RBuffer->P)->resolution & T30_RESOLUTION_R8_0770_OR_200) ? 0x0001 : 0x0000;
-                               if (plci->B3_prot == 5)
-                               {
-                                       if (!(fax_feature_bits & T30_FEATURE_BIT_ECM))
-                                               i |= 0x8000; /* This is not an ECM connection */
-                                       if (fax_feature_bits & T30_FEATURE_BIT_T6_CODING)
-                                               i |= 0x4000; /* This is a connection with MMR compression */
-                                       if (fax_feature_bits & T30_FEATURE_BIT_2D_CODING)
-                                               i |= 0x2000; /* This is a connection with MR compression */
-                                       if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
-                                               i |= 0x0004; /* More documents */
-                                       if (fax_feature_bits & T30_FEATURE_BIT_POLLING)
-                                               i |= 0x0002; /* Fax-polling indication */
-                               }
-                               dbug(1, dprintf("FAX Options %04x %04x", fax_feature_bits, i));
-                               PUT_WORD(&(plci->ncpi_buffer[3]), i);
-                               PUT_WORD(&(plci->ncpi_buffer[5]), ((T30_INFO *)plci->NL.RBuffer->P)->data_format);
-                               plci->ncpi_buffer[7] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_low;
-                               plci->ncpi_buffer[8] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_high;
-                               plci->ncpi_buffer[len] = 0;
-                               if (((T30_INFO *)plci->NL.RBuffer->P)->station_id_len)
-                               {
-                                       plci->ncpi_buffer[len] = 20;
-                                       for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++)
-                                               plci->ncpi_buffer[++len] = ((T30_INFO *)plci->NL.RBuffer->P)->station_id[i];
-                               }
-                               if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
-                               {
-                                       if (((T30_INFO *)plci->NL.RBuffer->P)->code < ARRAY_SIZE(fax_info))
-                                               info = fax_info[((T30_INFO *)plci->NL.RBuffer->P)->code];
-                                       else
-                                               info = _FAX_PROTOCOL_ERROR;
-                               }
-
-                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
-                                   & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
-                               {
-                                       i = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len;
-                                       while (i < plci->NL.RBuffer->length)
-                                               plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++];
-                               }
-
-                               plci->ncpi_buffer[0] = len;
-                               fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
-                               PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low, fax_feature_bits);
-
-                               plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND;
-                               if (((plci->NL.Ind & 0x0f) == N_CONNECT_ACK)
-                                   || (((plci->NL.Ind & 0x0f) == N_CONNECT)
-                                       && (fax_feature_bits & T30_FEATURE_BIT_POLLING))
-                                   || (((plci->NL.Ind & 0x0f) == N_EDATA)
-                                       && ((((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_TRAIN_OK)
-                                           || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
-                                           || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DTC))))
-                               {
-                                       plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT;
-                               }
-                               if (((plci->NL.Ind & 0x0f) == N_DISC)
-                                   || ((plci->NL.Ind & 0x0f) == N_DISC_ACK)
-                                   || (((plci->NL.Ind & 0x0f) == N_EDATA)
-                                       && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_EOP_CAPI)))
-                               {
-                                       plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
-                               }
-                       }
-                       break;
-
-               case B3_RTP:
-                       if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
-                       {
-                               if (plci->NL.RLength != 0)
-                               {
-                                       info = rtp_info[plci->NL.RBuffer->P[0]];
-                                       plci->ncpi_buffer[0] = plci->NL.RLength - 1;
-                                       for (i = 1; i < plci->NL.RLength; i++)
-                                               plci->ncpi_buffer[i] = plci->NL.RBuffer->P[i];
-                               }
-                       }
-                       break;
-
-               }
-               plci->NL.RNR = 2;
-       }
-       switch (plci->NL.Ind & 0x0f) {
-       case N_EDATA:
-               if ((plci->B3_prot == 4) || (plci->B3_prot == 5))
-               {
-                       dbug(1, dprintf("EDATA ncci=0x%x state=%d code=%02x", ncci, a->ncci_state[ncci],
-                                       ((T30_INFO *)plci->NL.RBuffer->P)->code));
-                       fax_send_edata_ack = (((T30_INFO *)(plci->fax_connect_info_buffer))->operating_mode == T30_OPERATING_MODE_CAPI_NEG);
-
-                       if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
-                           && (plci->nsf_control_bits & (T30_NSF_CONTROL_BIT_NEGOTIATE_IND | T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
-                           && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
-                           && (a->ncci_state[ncci] == OUTG_CON_PENDING)
-                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                           && !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
-                       {
-                               ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
-                               sendf(plci->appl, _MANUFACTURER_I, Id, 0, "dwbS", _DI_MANU_ID, _DI_NEGOTIATE_B3,
-                                     (byte)(plci->ncpi_buffer[0] + 1), plci->ncpi_buffer);
-                               plci->ncpi_state |= NCPI_NEGOTIATE_B3_SENT;
-                               if (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)
-                                       fax_send_edata_ack = false;
-                       }
-
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                       {
-                               switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
-                               {
-                               case EDATA_T30_DIS:
-                                       if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
-                                           && !(GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) & T30_CONTROL_BIT_REQUEST_POLLING)
-                                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                                           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                                       {
-                                               a->ncci_state[ncci] = INC_ACT_PENDING;
-                                               if (plci->B3_prot == 4)
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                                               else
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-                                               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                                       }
-                                       break;
-
-                               case EDATA_T30_TRAIN_OK:
-                                       if ((a->ncci_state[ncci] == INC_ACT_PENDING)
-                                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                                           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                                       {
-                                               if (plci->B3_prot == 4)
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                                               else
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-                                               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                                       }
-                                       break;
-
-                               case EDATA_T30_EOP_CAPI:
-                                       if (a->ncci_state[ncci] == CONNECTED)
-                                       {
-                                               sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", GOOD, plci->ncpi_buffer);
-                                               a->ncci_state[ncci] = INC_DIS_PENDING;
-                                               plci->ncpi_state = 0;
-                                               fax_send_edata_ack = false;
-                                       }
-                                       break;
-                               }
-                       }
-                       else
-                       {
-                               switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
-                               {
-                               case EDATA_T30_TRAIN_OK:
-                                       if ((a->ncci_state[ncci] == INC_ACT_PENDING)
-                                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                                           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                                       {
-                                               if (plci->B3_prot == 4)
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-                                               else
-                                                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-                                               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                                       }
-                                       break;
-                               }
-                       }
-                       if (fax_send_edata_ack)
-                       {
-                               ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
-                               plci->fax_edata_ack_length = 1;
-                               start_internal_command(Id, plci, fax_edata_ack_command);
-                       }
-               }
-               else
-               {
-                       dbug(1, dprintf("EDATA ncci=0x%x state=%d", ncci, a->ncci_state[ncci]));
-               }
-               break;
-       case N_CONNECT:
-               if (!a->ch_ncci[ch])
-               {
-                       ncci = get_ncci(plci, ch, 0);
-                       Id = (Id & 0xffff) | (((dword) ncci) << 16);
-               }
-               dbug(1, dprintf("N_CONNECT: ch=%d state=%d plci=%lx plci_Id=%lx plci_State=%d",
-                               ch, a->ncci_state[ncci], a->ncci_plci[ncci], plci->Id, plci->State));
-
-               msg = _CONNECT_B3_I;
-               if (a->ncci_state[ncci] == IDLE)
-                       plci->channels++;
-               else if (plci->B3_prot == 1)
-                       msg = _CONNECT_B3_T90_ACTIVE_I;
-
-               a->ncci_state[ncci] = INC_CON_PENDING;
-               if (plci->B3_prot == 4)
-                       sendf(plci->appl, msg, Id, 0, "s", "");
-               else
-                       sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
-               break;
-       case N_CONNECT_ACK:
-               dbug(1, dprintf("N_connect_Ack"));
-               if (plci->internal_command_queue[0]
-                   && ((plci->adjust_b_state == ADJUST_B_CONNECT_2)
-                       || (plci->adjust_b_state == ADJUST_B_CONNECT_3)
-                       || (plci->adjust_b_state == ADJUST_B_CONNECT_4)))
-               {
-                       (*(plci->internal_command_queue[0]))(Id, plci, 0);
-                       if (!plci->internal_command)
-                               next_internal_command(Id, plci);
-                       break;
-               }
-               msg = _CONNECT_B3_ACTIVE_I;
-               if (plci->B3_prot == 1)
-               {
-                       if (a->ncci_state[ncci] != OUTG_CON_PENDING)
-                               msg = _CONNECT_B3_T90_ACTIVE_I;
-                       a->ncci_state[ncci] = INC_ACT_PENDING;
-                       sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
-               }
-               else if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
-               {
-                       if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
-                           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-                           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-                       {
-                               a->ncci_state[ncci] = INC_ACT_PENDING;
-                               if (plci->B3_prot == 4)
-                                       sendf(plci->appl, msg, Id, 0, "s", "");
-                               else
-                                       sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
-                               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-                       }
-               }
-               else
-               {
-                       a->ncci_state[ncci] = INC_ACT_PENDING;
-                       sendf(plci->appl, msg, Id, 0, "S", plci->ncpi_buffer);
-               }
-               if (plci->adjust_b_restore)
-               {
-                       plci->adjust_b_restore = false;
-                       start_internal_command(Id, plci, adjust_b_restore);
-               }
-               break;
-       case N_DISC:
-       case N_DISC_ACK:
-               if (plci->internal_command_queue[0]
-                   && ((plci->internal_command == FAX_DISCONNECT_COMMAND_1)
-                       || (plci->internal_command == FAX_DISCONNECT_COMMAND_2)
-                       || (plci->internal_command == FAX_DISCONNECT_COMMAND_3)))
-               {
-                       (*(plci->internal_command_queue[0]))(Id, plci, 0);
-                       if (!plci->internal_command)
-                               next_internal_command(Id, plci);
-               }
-               ncci_state = a->ncci_state[ncci];
-               ncci_remove(plci, ncci, false);
-
-               /* with N_DISC or N_DISC_ACK the IDI frees the respective   */
-               /* channel, so we cannot store the state in ncci_state! The */
-               /* information which channel we received a N_DISC is thus   */
-               /* stored in the inc_dis_ncci_table buffer.                 */
-               for (i = 0; plci->inc_dis_ncci_table[i]; i++);
-               plci->inc_dis_ncci_table[i] = (byte) ncci;
-
-               /* need a connect_b3_ind before a disconnect_b3_ind with FAX */
-               if (!plci->channels
-                   && (plci->B1_resource == 16)
-                   && (plci->State <= CONNECTED))
-               {
-                       len = 9;
-                       i = ((T30_INFO *)plci->fax_connect_info_buffer)->rate_div_2400 * 2400;
-                       PUT_WORD(&plci->ncpi_buffer[1], i);
-                       PUT_WORD(&plci->ncpi_buffer[3], 0);
-                       i = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
-                       PUT_WORD(&plci->ncpi_buffer[5], i);
-                       PUT_WORD(&plci->ncpi_buffer[7], 0);
-                       plci->ncpi_buffer[len] = 0;
-                       plci->ncpi_buffer[0] = len;
-                       if (plci->B3_prot == 4)
-                               sendf(plci->appl, _CONNECT_B3_I, Id, 0, "s", "");
-                       else
-                       {
-
-                               if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id - 1])
-                                   & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
-                               {
-                                       plci->ncpi_buffer[++len] = 0;
-                                       plci->ncpi_buffer[++len] = 0;
-                                       plci->ncpi_buffer[++len] = 0;
-                                       plci->ncpi_buffer[0] = len;
-                               }
-
-                               sendf(plci->appl, _CONNECT_B3_I, Id, 0, "S", plci->ncpi_buffer);
-                       }
-                       sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer);
-                       plci->ncpi_state = 0;
-                       sig_req(plci, HANGUP, 0);
-                       send_req(plci);
-                       plci->State = OUTG_DIS_PENDING;
-                       /* disc here */
-               }
-               else if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                        && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
-                        && ((ncci_state == INC_DIS_PENDING) || (ncci_state == IDLE)))
-               {
-                       if (ncci_state == IDLE)
-                       {
-                               if (plci->channels)
-                                       plci->channels--;
-                               if ((plci->State == IDLE || plci->State == SUSPENDING) && !plci->channels) {
-                                       if (plci->State == SUSPENDING) {
-                                               sendf(plci->appl,
-                                                     _FACILITY_I,
-                                                     Id & 0xffffL,
-                                                     0,
-                                                     "ws", (word)3, "\x03\x04\x00\x00");
-                                               sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
-                                       }
-                                       plci_remove(plci);
-                                       plci->State = IDLE;
-                               }
-                       }
-               }
-               else if (plci->channels)
-               {
-                       sendf(plci->appl, _DISCONNECT_B3_I, Id, 0, "wS", info, plci->ncpi_buffer);
-                       plci->ncpi_state = 0;
-                       if ((ncci_state == OUTG_REJ_PENDING)
-                           && ((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)))
-                       {
-                               sig_req(plci, HANGUP, 0);
-                               send_req(plci);
-                               plci->State = OUTG_DIS_PENDING;
-                       }
-               }
-               break;
-       case N_RESET:
-               a->ncci_state[ncci] = INC_RES_PENDING;
-               sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer);
-               break;
-       case N_RESET_ACK:
-               a->ncci_state[ncci] = CONNECTED;
-               sendf(plci->appl, _RESET_B3_I, Id, 0, "S", plci->ncpi_buffer);
-               break;
-
-       case N_UDATA:
-               if (!(udata_forwarding_table[plci->NL.RBuffer->P[0] >> 5] & (1L << (plci->NL.RBuffer->P[0] & 0x1f))))
-               {
-                       plci->RData[0].P = plci->internal_ind_buffer + (-((int)(long)(plci->internal_ind_buffer)) & 3);
-                       plci->RData[0].PLength = INTERNAL_IND_BUFFER_SIZE;
-                       plci->NL.R = plci->RData;
-                       plci->NL.RNum = 1;
-                       return;
-               }
-               /* fall through */
-       case N_BDATA:
-       case N_DATA:
-               if (((a->ncci_state[ncci] != CONNECTED) && (plci->B2_prot == 1)) /* transparent */
-                   || (a->ncci_state[ncci] == IDLE)
-                   || (a->ncci_state[ncci] == INC_DIS_PENDING))
-               {
-                       plci->NL.RNR = 2;
-                       break;
-               }
-               if ((a->ncci_state[ncci] != CONNECTED)
-                   && (a->ncci_state[ncci] != OUTG_DIS_PENDING)
-                   && (a->ncci_state[ncci] != OUTG_REJ_PENDING))
-               {
-                       dbug(1, dprintf("flow control"));
-                       plci->NL.RNR = 1; /* flow control  */
-                       channel_x_off(plci, ch, 0);
-                       break;
-               }
-
-               NCCIcode = ncci | (((word)a->Id) << 8);
-
-               /* count all buffers within the Application pool    */
-               /* belonging to the same NCCI. If this is below the */
-               /* number of buffers available per NCCI we accept   */
-               /* this packet, otherwise we reject it              */
-               count = 0;
-               Num = 0xffff;
-               for (i = 0; i < APPLptr->MaxBuffer; i++) {
-                       if (NCCIcode == APPLptr->DataNCCI[i]) count++;
-                       if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i;
-               }
-
-               if (count >= APPLptr->MaxNCCIData || Num == 0xffff)
-               {
-                       dbug(3, dprintf("Flow-Control"));
-                       plci->NL.RNR = 1;
-                       if (++(APPLptr->NCCIDataFlowCtrlTimer) >=
-                           (word)((a->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) ? 40 : 2000))
-                       {
-                               plci->NL.RNR = 2;
-                               dbug(3, dprintf("DiscardData"));
-                       } else {
-                               channel_x_off(plci, ch, 0);
-                       }
-                       break;
-               }
-               else
-               {
-                       APPLptr->NCCIDataFlowCtrlTimer = 0;
-               }
-
-               plci->RData[0].P = ReceiveBufferGet(APPLptr, Num);
-               if (!plci->RData[0].P) {
-                       plci->NL.RNR = 1;
-                       channel_x_off(plci, ch, 0);
-                       break;
-               }
-
-               APPLptr->DataNCCI[Num] = NCCIcode;
-               APPLptr->DataFlags[Num] = (plci->Id << 8) | (plci->NL.Ind >> 4);
-               dbug(3, dprintf("Buffer(%d), Max = %d", Num, APPLptr->MaxBuffer));
-
-               plci->RNum = Num;
-               plci->RFlags = plci->NL.Ind >> 4;
-               plci->RData[0].PLength = APPLptr->MaxDataLength;
-               plci->NL.R = plci->RData;
-               if ((plci->NL.RLength != 0)
-                   && ((plci->B2_prot == B2_V120_ASYNC)
-                       || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
-                       || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
-               {
-                       plci->RData[1].P = plci->RData[0].P;
-                       plci->RData[1].PLength = plci->RData[0].PLength;
-                       plci->RData[0].P = v120_header_buffer + (-((unsigned long)v120_header_buffer) & 3);
-                       if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1))
-                               plci->RData[0].PLength = 1;
-                       else
-                               plci->RData[0].PLength = 2;
-                       if (plci->NL.RBuffer->P[0] & V120_HEADER_BREAK_BIT)
-                               plci->RFlags |= 0x0010;
-                       if (plci->NL.RBuffer->P[0] & (V120_HEADER_C1_BIT | V120_HEADER_C2_BIT))
-                               plci->RFlags |= 0x8000;
-                       plci->NL.RNum = 2;
-               }
-               else
-               {
-                       if ((plci->NL.Ind & 0x0f) == N_UDATA)
-                               plci->RFlags |= 0x0010;
-
-                       else if ((plci->B3_prot == B3_RTP) && ((plci->NL.Ind & 0x0f) == N_BDATA))
-                               plci->RFlags |= 0x0001;
-
-                       plci->NL.RNum = 1;
-               }
-               break;
-       case N_DATA_ACK:
-               data_ack(plci, ch);
-               break;
-       default:
-               plci->NL.RNR = 2;
-               break;
-       }
-}
-
-/*------------------------------------------------------------------*/
-/* find a free PLCI */
-/*------------------------------------------------------------------*/
-
-static word get_plci(DIVA_CAPI_ADAPTER *a)
-{
-       word i, j;
-       PLCI *plci;
-
-       for (i = 0; i < a->max_plci && a->plci[i].Id; i++);
-       if (i == a->max_plci) {
-               dbug(1, dprintf("get_plci: out of PLCIs"));
-               return 0;
-       }
-       plci = &a->plci[i];
-       plci->Id = (byte)(i + 1);
-
-       plci->Sig.Id = 0;
-       plci->NL.Id = 0;
-       plci->sig_req = 0;
-       plci->nl_req = 0;
-
-       plci->appl = NULL;
-       plci->relatedPTYPLCI = NULL;
-       plci->State = IDLE;
-       plci->SuppState = IDLE;
-       plci->channels = 0;
-       plci->tel = 0;
-       plci->B1_resource = 0;
-       plci->B2_prot = 0;
-       plci->B3_prot = 0;
-
-       plci->command = 0;
-       plci->m_command = 0;
-       init_internal_command_queue(plci);
-       plci->number = 0;
-       plci->req_in_start = 0;
-       plci->req_in = 0;
-       plci->req_out = 0;
-       plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
-       plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
-       plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
-
-       plci->data_sent = false;
-       plci->send_disc = 0;
-       plci->sig_global_req = 0;
-       plci->sig_remove_id = 0;
-       plci->nl_global_req = 0;
-       plci->nl_remove_id = 0;
-       plci->adv_nl = 0;
-       plci->manufacturer = false;
-       plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
-       plci->spoofed_msg = 0;
-       plci->ptyState = 0;
-       plci->cr_enquiry = false;
-       plci->hangup_flow_ctrl_timer = 0;
-
-       plci->ncci_ring_list = 0;
-       for (j = 0; j < MAX_CHANNELS_PER_PLCI; j++) plci->inc_dis_ncci_table[j] = 0;
-       bitmap_zero(plci->c_ind_mask_table, MAX_APPL);
-       bitmap_fill(plci->group_optimization_mask_table, MAX_APPL);
-       plci->fax_connect_info_length = 0;
-       plci->nsf_control_bits = 0;
-       plci->ncpi_state = 0x00;
-       plci->ncpi_buffer[0] = 0;
-
-       plci->requested_options_conn = 0;
-       plci->requested_options = 0;
-       plci->notifiedcall = 0;
-       plci->vswitchstate = 0;
-       plci->vsprot = 0;
-       plci->vsprotdialect = 0;
-       init_b1_config(plci);
-       dbug(1, dprintf("get_plci(%x)", plci->Id));
-       return i + 1;
-}
-
-/*------------------------------------------------------------------*/
-/* put a parameter in the parameter buffer                          */
-/*------------------------------------------------------------------*/
-
-static void add_p(PLCI *plci, byte code, byte *p)
-{
-       word p_length;
-
-       p_length = 0;
-       if (p) p_length = p[0];
-       add_ie(plci, code, p, p_length);
-}
-
-/*------------------------------------------------------------------*/
-/* put a structure in the parameter buffer                          */
-/*------------------------------------------------------------------*/
-static void add_s(PLCI *plci, byte code, API_PARSE *p)
-{
-       if (p) add_ie(plci, code, p->info, (word)p->length);
-}
-
-/*------------------------------------------------------------------*/
-/* put multiple structures in the parameter buffer                  */
-/*------------------------------------------------------------------*/
-static void add_ss(PLCI *plci, byte code, API_PARSE *p)
-{
-       byte i;
-
-       if (p) {
-               dbug(1, dprintf("add_ss(%x,len=%d)", code, p->length));
-               for (i = 2; i < (byte)p->length; i += p->info[i] + 2) {
-                       dbug(1, dprintf("add_ss_ie(%x,len=%d)", p->info[i - 1], p->info[i]));
-                       add_ie(plci, p->info[i - 1], (byte *)&(p->info[i]), (word)p->info[i]);
-               }
-       }
-}
-
-/*------------------------------------------------------------------*/
-/* return the channel number sent by the application in a esc_chi   */
-/*------------------------------------------------------------------*/
-static byte getChannel(API_PARSE *p)
-{
-       byte i;
-
-       if (p) {
-               for (i = 2; i < (byte)p->length; i += p->info[i] + 2) {
-                       if (p->info[i] == 2) {
-                               if (p->info[i - 1] == ESC && p->info[i + 1] == CHI) return (p->info[i + 2]);
-                       }
-               }
-       }
-       return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/* put an information element in the parameter buffer               */
-/*------------------------------------------------------------------*/
-
-static void add_ie(PLCI *plci, byte code, byte *p, word p_length)
-{
-       word i;
-
-       if (!(code & 0x80) && !p_length) return;
-
-       if (plci->req_in == plci->req_in_start) {
-               plci->req_in += 2;
-       }
-       else {
-               plci->req_in--;
-       }
-       plci->RBuffer[plci->req_in++] = code;
-
-       if (p) {
-               plci->RBuffer[plci->req_in++] = (byte)p_length;
-               for (i = 0; i < p_length; i++) plci->RBuffer[plci->req_in++] = p[1 + i];
-       }
-
-       plci->RBuffer[plci->req_in++] = 0;
-}
-
-/*------------------------------------------------------------------*/
-/* put a unstructured data into the buffer                          */
-/*------------------------------------------------------------------*/
-
-static void add_d(PLCI *plci, word length, byte *p)
-{
-       word i;
-
-       if (plci->req_in == plci->req_in_start) {
-               plci->req_in += 2;
-       }
-       else {
-               plci->req_in--;
-       }
-       for (i = 0; i < length; i++) plci->RBuffer[plci->req_in++] = p[i];
-}
-
-/*------------------------------------------------------------------*/
-/* put parameters from the Additional Info parameter in the         */
-/* parameter buffer                                                 */
-/*------------------------------------------------------------------*/
-
-static void add_ai(PLCI *plci, API_PARSE *ai)
-{
-       word i;
-       API_PARSE ai_parms[5];
-
-       for (i = 0; i < 5; i++) ai_parms[i].length = 0;
-
-       if (!ai->length)
-               return;
-       if (api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
-               return;
-
-       add_s(plci, KEY, &ai_parms[1]);
-       add_s(plci, UUI, &ai_parms[2]);
-       add_ss(plci, FTY, &ai_parms[3]);
-}
-
-/*------------------------------------------------------------------*/
-/* put parameter for b1 protocol in the parameter buffer            */
-/*------------------------------------------------------------------*/
-
-static word add_b1(PLCI *plci, API_PARSE *bp, word b_channel_info,
-                  word b1_facilities)
-{
-       API_PARSE bp_parms[8];
-       API_PARSE mdm_cfg[9];
-       API_PARSE global_config[2];
-       byte cai[256];
-       byte resource[] = {5, 9, 13, 12, 16, 39, 9, 17, 17, 18};
-       byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08";
-       word i;
-
-       API_PARSE mdm_cfg_v18[4];
-       word j, n, w;
-       dword d;
-
-
-       for (i = 0; i < 8; i++) bp_parms[i].length = 0;
-       for (i = 0; i < 2; i++) global_config[i].length = 0;
-
-       dbug(1, dprintf("add_b1"));
-       api_save_msg(bp, "s", &plci->B_protocol);
-
-       if (b_channel_info == 2) {
-               plci->B1_resource = 0;
-               adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
-               add_p(plci, CAI, "\x01\x00");
-               dbug(1, dprintf("Cai=1,0 (no resource)"));
-               return 0;
-       }
-
-       if (plci->tel == CODEC_PERMANENT) return 0;
-       else if (plci->tel == CODEC) {
-               plci->B1_resource = 1;
-               adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
-               add_p(plci, CAI, "\x01\x01");
-               dbug(1, dprintf("Cai=1,1 (Codec)"));
-               return 0;
-       }
-       else if (plci->tel == ADV_VOICE) {
-               plci->B1_resource = add_b1_facilities(plci, 9, (word)(b1_facilities | B1_FACILITY_VOICE));
-               adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities | B1_FACILITY_VOICE));
-               voice_cai[1] = plci->B1_resource;
-               PUT_WORD(&voice_cai[5], plci->appl->MaxDataLength);
-               add_p(plci, CAI, voice_cai);
-               dbug(1, dprintf("Cai=1,0x%x (AdvVoice)", voice_cai[1]));
-               return 0;
-       }
-       plci->call_dir &= ~(CALL_DIR_ORIGINATE | CALL_DIR_ANSWER);
-       if (plci->call_dir & CALL_DIR_OUT)
-               plci->call_dir |= CALL_DIR_ORIGINATE;
-       else if (plci->call_dir & CALL_DIR_IN)
-               plci->call_dir |= CALL_DIR_ANSWER;
-
-       if (!bp->length) {
-               plci->B1_resource = 0x5;
-               adjust_b1_facilities(plci, plci->B1_resource, b1_facilities);
-               add_p(plci, CAI, "\x01\x05");
-               return 0;
-       }
-
-       dbug(1, dprintf("b_prot_len=%d", (word)bp->length));
-       if (bp->length > 256) return _WRONG_MESSAGE_FORMAT;
-       if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
-       {
-               bp_parms[6].length = 0;
-               if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
-               {
-                       dbug(1, dprintf("b-form.!"));
-                       return _WRONG_MESSAGE_FORMAT;
-               }
-       }
-       else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
-       {
-               dbug(1, dprintf("b-form.!"));
-               return _WRONG_MESSAGE_FORMAT;
-       }
-
-       if (bp_parms[6].length)
-       {
-               if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
-               {
-                       return _WRONG_MESSAGE_FORMAT;
-               }
-               switch (GET_WORD(global_config[0].info))
-               {
-               case 1:
-                       plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
-                       break;
-               case 2:
-                       plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
-                       break;
-               }
-       }
-       dbug(1, dprintf("call_dir=%04x", plci->call_dir));
-
-
-       if ((GET_WORD(bp_parms[0].info) == B1_RTP)
-           && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
-       {
-               plci->B1_resource = add_b1_facilities(plci, 31, (word)(b1_facilities & ~B1_FACILITY_VOICE));
-               adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
-               cai[1] = plci->B1_resource;
-               cai[2] = 0;
-               cai[3] = 0;
-               cai[4] = 0;
-               PUT_WORD(&cai[5], plci->appl->MaxDataLength);
-               for (i = 0; i < bp_parms[3].length; i++)
-                       cai[7 + i] = bp_parms[3].info[1 + i];
-               cai[0] = 6 + bp_parms[3].length;
-               add_p(plci, CAI, cai);
-               return 0;
-       }
-
-
-       if ((GET_WORD(bp_parms[0].info) == B1_PIAFS)
-           && (plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))
-       {
-               plci->B1_resource = add_b1_facilities(plci, 35/* PIAFS HARDWARE FACILITY */, (word)(b1_facilities & ~B1_FACILITY_VOICE));
-               adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
-               cai[1] = plci->B1_resource;
-               cai[2] = 0;
-               cai[3] = 0;
-               cai[4] = 0;
-               PUT_WORD(&cai[5], plci->appl->MaxDataLength);
-               cai[0] = 6;
-               add_p(plci, CAI, cai);
-               return 0;
-       }
-
-
-       if ((GET_WORD(bp_parms[0].info) >= 32)
-           || (!((1L << GET_WORD(bp_parms[0].info)) & plci->adapter->profile.B1_Protocols)
-               && ((GET_WORD(bp_parms[0].info) != 3)
-                   || !((1L << B1_HDLC) & plci->adapter->profile.B1_Protocols)
-                   || ((bp_parms[3].length != 0) && (GET_WORD(&bp_parms[3].info[1]) != 0) && (GET_WORD(&bp_parms[3].info[1]) != 56000)))))
-       {
-               return _B1_NOT_SUPPORTED;
-       }
-       plci->B1_resource = add_b1_facilities(plci, resource[GET_WORD(bp_parms[0].info)],
-                                             (word)(b1_facilities & ~B1_FACILITY_VOICE));
-       adjust_b1_facilities(plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
-       cai[0] = 6;
-       cai[1] = plci->B1_resource;
-       for (i = 2; i < sizeof(cai); i++) cai[i] = 0;
-
-       if ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
-           || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
-           || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC))
-       { /* B1 - modem */
-               for (i = 0; i < 7; i++) mdm_cfg[i].length = 0;
-
-               if (bp_parms[3].length)
-               {
-                       if (api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwww", mdm_cfg))
-                       {
-                               return (_WRONG_MESSAGE_FORMAT);
-                       }
-
-                       cai[2] = 0; /* Bit rate for adaptation */
-
-                       dbug(1, dprintf("MDM Max Bit Rate:<%d>", GET_WORD(mdm_cfg[0].info)));
-
-                       PUT_WORD(&cai[13], 0);                          /* Min Tx speed */
-                       PUT_WORD(&cai[15], GET_WORD(mdm_cfg[0].info)); /* Max Tx speed */
-                       PUT_WORD(&cai[17], 0);                          /* Min Rx speed */
-                       PUT_WORD(&cai[19], GET_WORD(mdm_cfg[0].info)); /* Max Rx speed */
-
-                       cai[3] = 0; /* Async framing parameters */
-                       switch (GET_WORD(mdm_cfg[2].info))
-                       {       /* Parity     */
-                       case 1: /* odd parity */
-                               cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
-                               dbug(1, dprintf("MDM: odd parity"));
-                               break;
-
-                       case 2: /* even parity */
-                               cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
-                               dbug(1, dprintf("MDM: even parity"));
-                               break;
-
-                       default:
-                               dbug(1, dprintf("MDM: no parity"));
-                               break;
-                       }
-
-                       switch (GET_WORD(mdm_cfg[3].info))
-                       {       /* stop bits   */
-                       case 1: /* 2 stop bits */
-                               cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
-                               dbug(1, dprintf("MDM: 2 stop bits"));
-                               break;
-
-                       default:
-                               dbug(1, dprintf("MDM: 1 stop bit"));
-                               break;
-                       }
-
-                       switch (GET_WORD(mdm_cfg[1].info))
-                       {     /* char length */
-                       case 5:
-                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
-                               dbug(1, dprintf("MDM: 5 bits"));
-                               break;
-
-                       case 6:
-                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
-                               dbug(1, dprintf("MDM: 6 bits"));
-                               break;
-
-                       case 7:
-                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
-                               dbug(1, dprintf("MDM: 7 bits"));
-                               break;
-
-                       default:
-                               dbug(1, dprintf("MDM: 8 bits"));
-                               break;
-                       }
-
-                       cai[7] = 0; /* Line taking options */
-                       cai[8] = 0; /* Modulation negotiation options */
-                       cai[9] = 0; /* Modulation options */
-
-                       if (((plci->call_dir & CALL_DIR_ORIGINATE) != 0) ^ ((plci->call_dir & CALL_DIR_OUT) != 0))
-                       {
-                               cai[9] |= DSP_CAI_MODEM_REVERSE_DIRECTION;
-                               dbug(1, dprintf("MDM: Reverse direction"));
-                       }
-
-                       if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RETRAIN)
-                       {
-                               cai[9] |= DSP_CAI_MODEM_DISABLE_RETRAIN;
-                               dbug(1, dprintf("MDM: Disable retrain"));
-                       }
-
-                       if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_DISABLE_RING_TONE)
-                       {
-                               cai[7] |= DSP_CAI_MODEM_DISABLE_CALLING_TONE | DSP_CAI_MODEM_DISABLE_ANSWER_TONE;
-                               dbug(1, dprintf("MDM: Disable ring tone"));
-                       }
-
-                       if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_1800)
-                       {
-                               cai[8] |= DSP_CAI_MODEM_GUARD_TONE_1800HZ;
-                               dbug(1, dprintf("MDM: 1800 guard tone"));
-                       }
-                       else if (GET_WORD(mdm_cfg[4].info) & MDM_CAPI_GUARD_550)
-                       {
-                               cai[8] |= DSP_CAI_MODEM_GUARD_TONE_550HZ;
-                               dbug(1, dprintf("MDM: 550 guard tone"));
-                       }
-
-                       if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_V100)
-                       {
-                               cai[8] |= DSP_CAI_MODEM_NEGOTIATE_V100;
-                               dbug(1, dprintf("MDM: V100"));
-                       }
-                       else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_MOD_CLASS)
-                       {
-                               cai[8] |= DSP_CAI_MODEM_NEGOTIATE_IN_CLASS;
-                               dbug(1, dprintf("MDM: IN CLASS"));
-                       }
-                       else if ((GET_WORD(mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_DISABLED)
-                       {
-                               cai[8] |= DSP_CAI_MODEM_NEGOTIATE_DISABLED;
-                               dbug(1, dprintf("MDM: DISABLED"));
-                       }
-                       cai[0] = 20;
-
-                       if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_V18))
-                           && (GET_WORD(mdm_cfg[5].info) & 0x8000)) /* Private V.18 enable */
-                       {
-                               plci->requested_options |= 1L << PRIVATE_V18;
-                       }
-                       if (GET_WORD(mdm_cfg[5].info) & 0x4000) /* Private VOWN enable */
-                               plci->requested_options |= 1L << PRIVATE_VOWN;
-
-                       if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                           & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
-                       {
-                               if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwws", mdm_cfg))
-                               {
-                                       i = 27;
-                                       if (mdm_cfg[6].length >= 4)
-                                       {
-                                               d = GET_DWORD(&mdm_cfg[6].info[1]);
-                                               cai[7] |= (byte) d;          /* line taking options */
-                                               cai[9] |= (byte)(d >> 8);    /* modulation options */
-                                               cai[++i] = (byte)(d >> 16);  /* vown modulation options */
-                                               cai[++i] = (byte)(d >> 24);
-                                               if (mdm_cfg[6].length >= 8)
-                                               {
-                                                       d = GET_DWORD(&mdm_cfg[6].info[5]);
-                                                       cai[10] |= (byte) d;        /* disabled modulations mask */
-                                                       cai[11] |= (byte)(d >> 8);
-                                                       if (mdm_cfg[6].length >= 12)
-                                                       {
-                                                               d = GET_DWORD(&mdm_cfg[6].info[9]);
-                                                               cai[12] = (byte) d;          /* enabled modulations mask */
-                                                               cai[++i] = (byte)(d >> 8);   /* vown enabled modulations */
-                                                               cai[++i] = (byte)(d >> 16);
-                                                               cai[++i] = (byte)(d >> 24);
-                                                               cai[++i] = 0;
-                                                               if (mdm_cfg[6].length >= 14)
-                                                               {
-                                                                       w = GET_WORD(&mdm_cfg[6].info[13]);
-                                                                       if (w != 0)
-                                                                               PUT_WORD(&cai[13], w);  /* min tx speed */
-                                                                       if (mdm_cfg[6].length >= 16)
-                                                                       {
-                                                                               w = GET_WORD(&mdm_cfg[6].info[15]);
-                                                                               if (w != 0)
-                                                                                       PUT_WORD(&cai[15], w);  /* max tx speed */
-                                                                               if (mdm_cfg[6].length >= 18)
-                                                                               {
-                                                                                       w = GET_WORD(&mdm_cfg[6].info[17]);
-                                                                                       if (w != 0)
-                                                                                               PUT_WORD(&cai[17], w);  /* min rx speed */
-                                                                                       if (mdm_cfg[6].length >= 20)
-                                                                                       {
-                                                                                               w = GET_WORD(&mdm_cfg[6].info[19]);
-                                                                                               if (w != 0)
-                                                                                                       PUT_WORD(&cai[19], w);  /* max rx speed */
-                                                                                               if (mdm_cfg[6].length >= 22)
-                                                                                               {
-                                                                                                       w = GET_WORD(&mdm_cfg[6].info[21]);
-                                                                                                       cai[23] = (byte)(-((short) w));  /* transmit level */
-                                                                                                       if (mdm_cfg[6].length >= 24)
-                                                                                                       {
-                                                                                                               w = GET_WORD(&mdm_cfg[6].info[23]);
-                                                                                                               cai[22] |= (byte) w;        /* info options mask */
-                                                                                                               cai[21] |= (byte)(w >> 8);  /* disabled symbol rates */
-                                                                                                       }
-                                                                                               }
-                                                                                       }
-                                                                               }
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                       }
-                                       cai[27] = i - 27;
-                                       i++;
-                                       if (!api_parse(&bp_parms[3].info[1], (word)bp_parms[3].length, "wwwwwwss", mdm_cfg))
-                                       {
-                                               if (!api_parse(&mdm_cfg[7].info[1], (word)mdm_cfg[7].length, "sss", mdm_cfg_v18))
-                                               {
-                                                       for (n = 0; n < 3; n++)
-                                                       {
-                                                               cai[i] = (byte)(mdm_cfg_v18[n].length);
-                                                               for (j = 1; j < ((word)(cai[i] + 1)); j++)
-                                                                       cai[i + j] = mdm_cfg_v18[n].info[j];
-                                                               i += cai[i] + 1;
-                                                       }
-                                               }
-                                       }
-                                       cai[0] = (byte)(i - 1);
-                               }
-                       }
-
-               }
-       }
-       if (GET_WORD(bp_parms[0].info) == 2 ||                         /* V.110 async */
-           GET_WORD(bp_parms[0].info) == 3)                           /* V.110 sync */
-       {
-               if (bp_parms[3].length) {
-                       dbug(1, dprintf("V.110,%d", GET_WORD(&bp_parms[3].info[1])));
-                       switch (GET_WORD(&bp_parms[3].info[1])) {                 /* Rate */
-                       case 0:
-                       case 56000:
-                               if (GET_WORD(bp_parms[0].info) == 3) {                  /* V.110 sync 56k */
-                                       dbug(1, dprintf("56k sync HSCX"));
-                                       cai[1] = 8;
-                                       cai[2] = 0;
-                                       cai[3] = 0;
-                               }
-                               else if (GET_WORD(bp_parms[0].info) == 2) {
-                                       dbug(1, dprintf("56k async DSP"));
-                                       cai[2] = 9;
-                               }
-                               break;
-                       case 50:     cai[2] = 1;  break;
-                       case 75:     cai[2] = 1;  break;
-                       case 110:    cai[2] = 1;  break;
-                       case 150:    cai[2] = 1;  break;
-                       case 200:    cai[2] = 1;  break;
-                       case 300:    cai[2] = 1;  break;
-                       case 600:    cai[2] = 1;  break;
-                       case 1200:   cai[2] = 2;  break;
-                       case 2400:   cai[2] = 3;  break;
-                       case 4800:   cai[2] = 4;  break;
-                       case 7200:   cai[2] = 10; break;
-                       case 9600:   cai[2] = 5;  break;
-                       case 12000:  cai[2] = 13; break;
-                       case 24000:  cai[2] = 0;  break;
-                       case 14400:  cai[2] = 11; break;
-                       case 19200:  cai[2] = 6;  break;
-                       case 28800:  cai[2] = 12; break;
-                       case 38400:  cai[2] = 7;  break;
-                       case 48000:  cai[2] = 8;  break;
-                       case 76:     cai[2] = 15; break;  /* 75/1200     */
-                       case 1201:   cai[2] = 14; break;  /* 1200/75     */
-                       case 56001:  cai[2] = 9;  break;  /* V.110 56000 */
-
-                       default:
-                               return _B1_PARM_NOT_SUPPORTED;
-                       }
-                       cai[3] = 0;
-                       if (cai[1] == 13)                                        /* v.110 async */
-                       {
-                               if (bp_parms[3].length >= 8)
-                               {
-                                       switch (GET_WORD(&bp_parms[3].info[3]))
-                                       {       /* char length */
-                                       case 5:
-                                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
-                                               break;
-                                       case 6:
-                                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
-                                               break;
-                                       case 7:
-                                               cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
-                                               break;
-                                       }
-                                       switch (GET_WORD(&bp_parms[3].info[5]))
-                                       {       /* Parity     */
-                                       case 1: /* odd parity */
-                                               cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
-                                               break;
-                                       case 2: /* even parity */
-                                               cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
-                                               break;
-                                       }
-                                       switch (GET_WORD(&bp_parms[3].info[7]))
-                                       {       /* stop bits   */
-                                       case 1: /* 2 stop bits */
-                                               cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               else if (cai[1] == 8 || GET_WORD(bp_parms[0].info) == 3) {
-                       dbug(1, dprintf("V.110 default 56k sync"));
-                       cai[1] = 8;
-                       cai[2] = 0;
-                       cai[3] = 0;
-               }
-               else {
-                       dbug(1, dprintf("V.110 default 9600 async"));
-                       cai[2] = 5;
-               }
-       }
-       PUT_WORD(&cai[5], plci->appl->MaxDataLength);
-       dbug(1, dprintf("CAI[%d]=%x,%x,%x,%x,%x,%x", cai[0], cai[1], cai[2], cai[3], cai[4], cai[5], cai[6]));
-/* HexDump ("CAI", sizeof(cai), &cai[0]); */
-
-       add_p(plci, CAI, cai);
-       return 0;
-}
-
-/*------------------------------------------------------------------*/
-/* put parameter for b2 and B3  protocol in the parameter buffer    */
-/*------------------------------------------------------------------*/
-
-static word add_b23(PLCI *plci, API_PARSE *bp)
-{
-       word i, fax_control_bits;
-       byte pos, len;
-       byte SAPI = 0x40;  /* default SAPI 16 for x.31 */
-       API_PARSE bp_parms[8];
-       API_PARSE *b1_config;
-       API_PARSE *b2_config;
-       API_PARSE b2_config_parms[8];
-       API_PARSE *b3_config;
-       API_PARSE b3_config_parms[6];
-       API_PARSE global_config[2];
-
-       static byte llc[3] = {2,0,0};
-       static byte dlc[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-       static byte nlc[256];
-       static byte lli[12] = {1,1};
-
-       const byte llc2_out[] = {1,2,4,6,2,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
-       const byte llc2_in[]  = {1,3,4,6,3,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
-
-       const byte llc3[] = {4,3,2,2,6,6,0};
-       const byte header[] = {0,2,3,3,0,0,0};
-
-       for (i = 0; i < 8; i++) bp_parms[i].length = 0;
-       for (i = 0; i < 6; i++) b2_config_parms[i].length = 0;
-       for (i = 0; i < 5; i++) b3_config_parms[i].length = 0;
-
-       lli[0] = 1;
-       lli[1] = 1;
-       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
-               lli[1] |= 2;
-       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
-               lli[1] |= 4;
-
-       if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
-               lli[1] |= 0x10;
-               if (plci->rx_dma_descriptor <= 0) {
-                       plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic);
-                       if (plci->rx_dma_descriptor >= 0)
-                               plci->rx_dma_descriptor++;
-               }
-               if (plci->rx_dma_descriptor > 0) {
-                       lli[0] = 6;
-                       lli[1] |= 0x40;
-                       lli[2] = (byte)(plci->rx_dma_descriptor - 1);
-                       lli[3] = (byte)plci->rx_dma_magic;
-                       lli[4] = (byte)(plci->rx_dma_magic >>  8);
-                       lli[5] = (byte)(plci->rx_dma_magic >> 16);
-                       lli[6] = (byte)(plci->rx_dma_magic >> 24);
-               }
-       }
-
-       if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
-               lli[1] |= 0x20;
-       }
-
-       dbug(1, dprintf("add_b23"));
-       api_save_msg(bp, "s", &plci->B_protocol);
-
-       if (!bp->length && plci->tel)
-       {
-               plci->adv_nl = true;
-               dbug(1, dprintf("Default adv.Nl"));
-               add_p(plci, LLI, lli);
-               plci->B2_prot = 1 /*XPARENT*/;
-               plci->B3_prot = 0 /*XPARENT*/;
-               llc[1] = 2;
-               llc[2] = 4;
-               add_p(plci, LLC, llc);
-               dlc[0] = 2;
-               PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
-               add_p(plci, DLC, dlc);
-               return 0;
-       }
-
-       if (!bp->length) /*default*/
-       {
-               dbug(1, dprintf("ret default"));
-               add_p(plci, LLI, lli);
-               plci->B2_prot = 0 /*X.75   */;
-               plci->B3_prot = 0 /*XPARENT*/;
-               llc[1] = 1;
-               llc[2] = 4;
-               add_p(plci, LLC, llc);
-               dlc[0] = 2;
-               PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
-               add_p(plci, DLC, dlc);
-               return 0;
-       }
-       dbug(1, dprintf("b_prot_len=%d", (word)bp->length));
-       if ((word)bp->length > 256)    return _WRONG_MESSAGE_FORMAT;
-
-       if (api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
-       {
-               bp_parms[6].length = 0;
-               if (api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
-               {
-                       dbug(1, dprintf("b-form.!"));
-                       return _WRONG_MESSAGE_FORMAT;
-               }
-       }
-       else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
-       {
-               dbug(1, dprintf("b-form.!"));
-               return _WRONG_MESSAGE_FORMAT;
-       }
-
-       if (plci->tel == ADV_VOICE) /* transparent B on advanced voice */
-       {
-               if (GET_WORD(bp_parms[1].info) != 1
-                   || GET_WORD(bp_parms[2].info) != 0) return _B2_NOT_SUPPORTED;
-               plci->adv_nl = true;
-       }
-       else if (plci->tel) return _B2_NOT_SUPPORTED;
-
-
-       if ((GET_WORD(bp_parms[1].info) == B2_RTP)
-           && (GET_WORD(bp_parms[2].info) == B3_RTP)
-           && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
-       {
-               add_p(plci, LLI, lli);
-               plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
-               plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
-               llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? 14 : 13;
-               llc[2] = 4;
-               add_p(plci, LLC, llc);
-               dlc[0] = 2;
-               PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
-               dlc[3] = 3; /* Addr A */
-               dlc[4] = 1; /* Addr B */
-               dlc[5] = 7; /* modulo mode */
-               dlc[6] = 7; /* window size */
-               dlc[7] = 0; /* XID len Lo  */
-               dlc[8] = 0; /* XID len Hi  */
-               for (i = 0; i < bp_parms[4].length; i++)
-                       dlc[9 + i] = bp_parms[4].info[1 + i];
-               dlc[0] = (byte)(8 + bp_parms[4].length);
-               add_p(plci, DLC, dlc);
-               for (i = 0; i < bp_parms[5].length; i++)
-                       nlc[1 + i] = bp_parms[5].info[1 + i];
-               nlc[0] = (byte)(bp_parms[5].length);
-               add_p(plci, NLC, nlc);
-               return 0;
-       }
-
-
-
-       if ((GET_WORD(bp_parms[1].info) >= 32)
-           || (!((1L << GET_WORD(bp_parms[1].info)) & plci->adapter->profile.B2_Protocols)
-               && ((GET_WORD(bp_parms[1].info) != B2_PIAFS)
-                   || !(plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))))
-
-       {
-               return _B2_NOT_SUPPORTED;
-       }
-       if ((GET_WORD(bp_parms[2].info) >= 32)
-           || !((1L << GET_WORD(bp_parms[2].info)) & plci->adapter->profile.B3_Protocols))
-       {
-               return _B3_NOT_SUPPORTED;
-       }
-       if ((GET_WORD(bp_parms[1].info) != B2_SDLC)
-           && ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
-               || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
-               || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC)))
-       {
-               return (add_modem_b23(plci, bp_parms));
-       }
-
-       add_p(plci, LLI, lli);
-
-       plci->B2_prot = (byte)GET_WORD(bp_parms[1].info);
-       plci->B3_prot = (byte)GET_WORD(bp_parms[2].info);
-       if (plci->B2_prot == 12) SAPI = 0; /* default SAPI D-channel */
-
-       if (bp_parms[6].length)
-       {
-               if (api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
-               {
-                       return _WRONG_MESSAGE_FORMAT;
-               }
-               switch (GET_WORD(global_config[0].info))
-               {
-               case 1:
-                       plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
-                       break;
-               case 2:
-                       plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
-                       break;
-               }
-       }
-       dbug(1, dprintf("call_dir=%04x", plci->call_dir));
-
-
-       if (plci->B2_prot == B2_PIAFS)
-               llc[1] = PIAFS_CRC;
-       else
-/* IMPLEMENT_PIAFS */
-       {
-               llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
-                       llc2_out[GET_WORD(bp_parms[1].info)] : llc2_in[GET_WORD(bp_parms[1].info)];
-       }
-       llc[2] = llc3[GET_WORD(bp_parms[2].info)];
-
-       add_p(plci, LLC, llc);
-
-       dlc[0] = 2;
-       PUT_WORD(&dlc[1], plci->appl->MaxDataLength +
-                header[GET_WORD(bp_parms[2].info)]);
-
-       b1_config = &bp_parms[3];
-       nlc[0] = 0;
-       if (plci->B3_prot == 4
-           || plci->B3_prot == 5)
-       {
-               for (i = 0; i < sizeof(T30_INFO); i++) nlc[i] = 0;
-               nlc[0] = sizeof(T30_INFO);
-               if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                       ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI;
-               ((T30_INFO *)&nlc[1])->rate_div_2400 = 0xff;
-               if (b1_config->length >= 2)
-               {
-                       ((T30_INFO *)&nlc[1])->rate_div_2400 = (byte)(GET_WORD(&b1_config->info[1]) / 2400);
-               }
-       }
-       b2_config = &bp_parms[4];
-
-
-       if (llc[1] == PIAFS_CRC)
-       {
-               if (plci->B3_prot != B3_TRANSPARENT)
-               {
-                       return _B_STACK_NOT_SUPPORTED;
-               }
-               if (b2_config->length && api_parse(&b2_config->info[1], (word)b2_config->length, "bwww", b2_config_parms)) {
-                       return _WRONG_MESSAGE_FORMAT;
-               }
-               PUT_WORD(&dlc[1], plci->appl->MaxDataLength);
-               dlc[3] = 0; /* Addr A */
-               dlc[4] = 0; /* Addr B */
-               dlc[5] = 0; /* modulo mode */
-               dlc[6] = 0; /* window size */
-               if (b2_config->length >= 7) {
-                       dlc[7] = 7;
-                       dlc[8] = 0;
-                       dlc[9] = b2_config_parms[0].info[0]; /* PIAFS protocol Speed configuration */
-                       dlc[10] = b2_config_parms[1].info[0]; /* V.42bis P0 */
-                       dlc[11] = b2_config_parms[1].info[1]; /* V.42bis P0 */
-                       dlc[12] = b2_config_parms[2].info[0]; /* V.42bis P1 */
-                       dlc[13] = b2_config_parms[2].info[1]; /* V.42bis P1 */
-                       dlc[14] = b2_config_parms[3].info[0]; /* V.42bis P2 */
-                       dlc[15] = b2_config_parms[3].info[1]; /* V.42bis P2 */
-                       dlc[0] = 15;
-                       if (b2_config->length >= 8) { /* PIAFS control abilities */
-                               dlc[7] = 10;
-                               dlc[16] = 2; /* Length of PIAFS extension */
-                               dlc[17] = PIAFS_UDATA_ABILITIES; /* control (UDATA) ability */
-                               dlc[18] = b2_config_parms[4].info[0]; /* value */
-                               dlc[0] = 18;
-                       }
-               }
-               else /* default values, 64K, variable, no compression */
-               {
-                       dlc[7] = 7;
-                       dlc[8] = 0;
-                       dlc[9] = 0x03; /* PIAFS protocol Speed configuration */
-                       dlc[10] = 0x03; /* V.42bis P0 */
-                       dlc[11] = 0;    /* V.42bis P0 */
-                       dlc[12] = 0;    /* V.42bis P1 */
-                       dlc[13] = 0;    /* V.42bis P1 */
-                       dlc[14] = 0;    /* V.42bis P2 */
-                       dlc[15] = 0;    /* V.42bis P2 */
-                       dlc[0] = 15;
-               }
-               add_p(plci, DLC, dlc);
-       }
-       else
-
-               if ((llc[1] == V120_L2) || (llc[1] == V120_V42BIS))
-               {
-                       if (plci->B3_prot != B3_TRANSPARENT)
-                               return _B_STACK_NOT_SUPPORTED;
-
-                       dlc[0] = 6;
-                       PUT_WORD(&dlc[1], GET_WORD(&dlc[1]) + 2);
-                       dlc[3] = 0x08;
-                       dlc[4] = 0x01;
-                       dlc[5] = 127;
-                       dlc[6] = 7;
-                       if (b2_config->length != 0)
-                       {
-                               if ((llc[1] == V120_V42BIS) && api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) {
-                                       return _WRONG_MESSAGE_FORMAT;
-                               }
-                               dlc[3] = (byte)((b2_config->info[2] << 3) | ((b2_config->info[1] >> 5) & 0x04));
-                               dlc[4] = (byte)((b2_config->info[1] << 1) | 0x01);
-                               if (b2_config->info[3] != 128)
-                               {
-                                       dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
-                                       return _B2_PARM_NOT_SUPPORTED;
-                               }
-                               dlc[5] = (byte)(b2_config->info[3] - 1);
-                               dlc[6] = b2_config->info[4];
-                               if (llc[1] == V120_V42BIS) {
-                                       if (b2_config->length >= 10) {
-                                               dlc[7] = 6;
-                                               dlc[8] = 0;
-                                               dlc[9] = b2_config_parms[4].info[0];
-                                               dlc[10] = b2_config_parms[4].info[1];
-                                               dlc[11] = b2_config_parms[5].info[0];
-                                               dlc[12] = b2_config_parms[5].info[1];
-                                               dlc[13] = b2_config_parms[6].info[0];
-                                               dlc[14] = b2_config_parms[6].info[1];
-                                               dlc[0] = 14;
-                                               dbug(1, dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
-                                               dbug(1, dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
-                                               dbug(1, dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
-                                       }
-                                       else {
-                                               dlc[6] = 14;
-                                       }
-                               }
-                       }
-               }
-               else
-               {
-                       if (b2_config->length)
-                       {
-                               dbug(1, dprintf("B2-Config"));
-                               if (llc[1] == X75_V42BIS) {
-                                       if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms))
-                                       {
-                                               return _WRONG_MESSAGE_FORMAT;
-                                       }
-                               }
-                               else {
-                                       if (api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbs", b2_config_parms))
-                                       {
-                                               return _WRONG_MESSAGE_FORMAT;
-                                       }
-                               }
-                               /* if B2 Protocol is LAPD, b2_config structure is different */
-                               if (llc[1] == 6)
-                               {
-                                       dlc[0] = 4;
-                                       if (b2_config->length >= 1) dlc[2] = b2_config->info[1];      /* TEI */
-                                       else dlc[2] = 0x01;
-                                       if ((b2_config->length >= 2) && (plci->B2_prot == 12))
-                                       {
-                                               SAPI = b2_config->info[2];    /* SAPI */
-                                       }
-                                       dlc[1] = SAPI;
-                                       if ((b2_config->length >= 3) && (b2_config->info[3] == 128))
-                                       {
-                                               dlc[3] = 127;      /* Mode */
-                                       }
-                                       else
-                                       {
-                                               dlc[3] = 7;        /* Mode */
-                                       }
-
-                                       if (b2_config->length >= 4) dlc[4] = b2_config->info[4];      /* Window */
-                                       else dlc[4] = 1;
-                                       dbug(1, dprintf("D-dlc[%d]=%x,%x,%x,%x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
-                                       if (b2_config->length > 5) return _B2_PARM_NOT_SUPPORTED;
-                               }
-                               else
-                               {
-                                       dlc[0] = (byte)(b2_config_parms[4].length + 6);
-                                       dlc[3] = b2_config->info[1];
-                                       dlc[4] = b2_config->info[2];
-                                       if (b2_config->info[3] != 8 && b2_config->info[3] != 128) {
-                                               dbug(1, dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
-                                               return _B2_PARM_NOT_SUPPORTED;
-                                       }
-
-                                       dlc[5] = (byte)(b2_config->info[3] - 1);
-                                       dlc[6] = b2_config->info[4];
-                                       if (dlc[6] > dlc[5]) {
-                                               dbug(1, dprintf("2D-dlc= %x %x %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4], dlc[5], dlc[6]));
-                                               return _B2_PARM_NOT_SUPPORTED;
-                                       }
-
-                                       if (llc[1] == X75_V42BIS) {
-                                               if (b2_config->length >= 10) {
-                                                       dlc[7] = 6;
-                                                       dlc[8] = 0;
-                                                       dlc[9] = b2_config_parms[4].info[0];
-                                                       dlc[10] = b2_config_parms[4].info[1];
-                                                       dlc[11] = b2_config_parms[5].info[0];
-                                                       dlc[12] = b2_config_parms[5].info[1];
-                                                       dlc[13] = b2_config_parms[6].info[0];
-                                                       dlc[14] = b2_config_parms[6].info[1];
-                                                       dlc[0] = 14;
-                                                       dbug(1, dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
-                                                       dbug(1, dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
-                                                       dbug(1, dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
-                                               }
-                                               else {
-                                                       dlc[6] = 14;
-                                               }
-
-                                       }
-                                       else {
-                                               PUT_WORD(&dlc[7], (word)b2_config_parms[4].length);
-                                               for (i = 0; i < b2_config_parms[4].length; i++)
-                                                       dlc[11 + i] = b2_config_parms[4].info[1 + i];
-                                       }
-                               }
-                       }
-               }
-       add_p(plci, DLC, dlc);
-
-       b3_config = &bp_parms[5];
-       if (b3_config->length)
-       {
-               if (plci->B3_prot == 4
-                   || plci->B3_prot == 5)
-               {
-                       if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwss", b3_config_parms))
-                       {
-                               return _WRONG_MESSAGE_FORMAT;
-                       }
-                       i = GET_WORD((byte *)(b3_config_parms[0].info));
-                       ((T30_INFO *)&nlc[1])->resolution = (byte)(((i & 0x0001) ||
-                                                                   ((plci->B3_prot == 4) && (((byte)(GET_WORD((byte *)b3_config_parms[1].info))) != 5))) ? T30_RESOLUTION_R8_0770_OR_200 : 0);
-                       ((T30_INFO *)&nlc[1])->data_format = (byte)(GET_WORD((byte *)b3_config_parms[1].info));
-                       fax_control_bits = T30_CONTROL_BIT_ALL_FEATURES;
-                       if ((((T30_INFO *)&nlc[1])->rate_div_2400 != 0) && (((T30_INFO *)&nlc[1])->rate_div_2400 <= 6))
-                               fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_V34FAX;
-                       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
-                       {
-
-                               if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                                   & (1L << PRIVATE_FAX_PAPER_FORMATS))
-                               {
-                                       ((T30_INFO *)&nlc[1])->resolution |= T30_RESOLUTION_R8_1540 |
-                                               T30_RESOLUTION_R16_1540_OR_400 | T30_RESOLUTION_300_300 |
-                                               T30_RESOLUTION_INCH_BASED | T30_RESOLUTION_METRIC_BASED;
-                               }
-
-                               ((T30_INFO *)&nlc[1])->recording_properties =
-                                       T30_RECORDING_WIDTH_ISO_A3 |
-                                       (T30_RECORDING_LENGTH_UNLIMITED << 2) |
-                                       (T30_MIN_SCANLINE_TIME_00_00_00 << 4);
-                       }
-                       if (plci->B3_prot == 5)
-                       {
-                               if (i & 0x0002) /* Accept incoming fax-polling requests */
-                                       fax_control_bits |= T30_CONTROL_BIT_ACCEPT_POLLING;
-                               if (i & 0x2000) /* Do not use MR compression */
-                                       fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_2D_CODING;
-                               if (i & 0x4000) /* Do not use MMR compression */
-                                       fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_T6_CODING;
-                               if (i & 0x8000) /* Do not use ECM */
-                                       fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_ECM;
-                               if (plci->fax_connect_info_length != 0)
-                               {
-                                       ((T30_INFO *)&nlc[1])->resolution = ((T30_INFO *)plci->fax_connect_info_buffer)->resolution;
-                                       ((T30_INFO *)&nlc[1])->data_format = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
-                                       ((T30_INFO *)&nlc[1])->recording_properties = ((T30_INFO *)plci->fax_connect_info_buffer)->recording_properties;
-                                       fax_control_bits |= GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) &
-                                               (T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
-                               }
-                       }
-                       /* copy station id to NLC */
-                       for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++)
-                       {
-                               if (i < b3_config_parms[2].length)
-                               {
-                                       ((T30_INFO *)&nlc[1])->station_id[i] = ((byte *)b3_config_parms[2].info)[1 + i];
-                               }
-                               else
-                               {
-                                       ((T30_INFO *)&nlc[1])->station_id[i] = ' ';
-                               }
-                       }
-                       ((T30_INFO *)&nlc[1])->station_id_len = T30_MAX_STATION_ID_LENGTH;
-                       /* copy head line to NLC */
-                       if (b3_config_parms[3].length)
-                       {
-
-                               pos = (byte)(fax_head_line_time(&(((T30_INFO *)&nlc[1])->station_id[T30_MAX_STATION_ID_LENGTH])));
-                               if (pos != 0)
-                               {
-                                       if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE)
-                                               pos = 0;
-                                       else
-                                       {
-                                               nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
-                                               nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
-                                               len = (byte)b3_config_parms[2].length;
-                                               if (len > 20)
-                                                       len = 20;
-                                               if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE)
-                                               {
-                                                       for (i = 0; i < len; i++)
-                                                               nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[2].info)[1 + i];
-                                                       nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
-                                                       nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' ';
-                                               }
-                                       }
-                               }
-
-                               len = (byte)b3_config_parms[3].length;
-                               if (len > CAPI_MAX_HEAD_LINE_SPACE - pos)
-                                       len = (byte)(CAPI_MAX_HEAD_LINE_SPACE - pos);
-                               ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len);
-                               nlc[0] += (byte)(pos + len);
-                               for (i = 0; i < len; i++)
-                                       nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] =  ((byte *)b3_config_parms[3].info)[1 + i];
-                       } else
-                               ((T30_INFO *)&nlc[1])->head_line_len = 0;
-
-                       plci->nsf_control_bits = 0;
-                       if (plci->B3_prot == 5)
-                       {
-                               if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
-                                   && (GET_WORD((byte *)b3_config_parms[1].info) & 0x8000)) /* Private SUB/SEP/PWD enable */
-                               {
-                                       plci->requested_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
-                               }
-                               if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
-                                   && (GET_WORD((byte *)b3_config_parms[1].info) & 0x4000)) /* Private non-standard facilities enable */
-                               {
-                                       plci->requested_options |= 1L << PRIVATE_FAX_NONSTANDARD;
-                               }
-                               if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                                   & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
-                               {
-                                       if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                                           & (1L << PRIVATE_FAX_SUB_SEP_PWD))
-                                       {
-                                               fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
-                                               if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
-                                                       fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
-                                       }
-                                       len = nlc[0];
-                                       pos = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
-                                       if (pos < plci->fax_connect_info_length)
-                                       {
-                                               for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
-                                                       nlc[++len] = plci->fax_connect_info_buffer[pos++];
-                                       }
-                                       else
-                                               nlc[++len] = 0;
-                                       if (pos < plci->fax_connect_info_length)
-                                       {
-                                               for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
-                                                       nlc[++len] = plci->fax_connect_info_buffer[pos++];
-                                       }
-                                       else
-                                               nlc[++len] = 0;
-                                       if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id - 1])
-                                           & (1L << PRIVATE_FAX_NONSTANDARD))
-                                       {
-                                               if ((pos < plci->fax_connect_info_length) && (plci->fax_connect_info_buffer[pos] != 0))
-                                               {
-                                                       if ((plci->fax_connect_info_buffer[pos] >= 3) && (plci->fax_connect_info_buffer[pos + 1] >= 2))
-                                                               plci->nsf_control_bits = GET_WORD(&plci->fax_connect_info_buffer[pos + 2]);
-                                                       for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
-                                                               nlc[++len] = plci->fax_connect_info_buffer[pos++];
-                                               }
-                                               else
-                                               {
-                                                       if (api_parse(&b3_config->info[1], (word)b3_config->length, "wwsss", b3_config_parms))
-                                                       {
-                                                               dbug(1, dprintf("non-standard facilities info missing or wrong format"));
-                                                               nlc[++len] = 0;
-                                                       }
-                                                       else
-                                                       {
-                                                               if ((b3_config_parms[4].length >= 3) && (b3_config_parms[4].info[1] >= 2))
-                                                                       plci->nsf_control_bits = GET_WORD(&b3_config_parms[4].info[2]);
-                                                               nlc[++len] = (byte)(b3_config_parms[4].length);
-                                                               for (i = 0; i < b3_config_parms[4].length; i++)
-                                                                       nlc[++len] = b3_config_parms[4].info[1 + i];
-                                                       }
-                                               }
-                                       }
-                                       nlc[0] = len;
-                                       if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
-                                           && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
-                                       {
-                                               ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI_NEG;
-                                       }
-                               }
-                       }
-
-                       PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits);
-                       len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH;
-                       for (i = 0; i < len; i++)
-                               plci->fax_connect_info_buffer[i] = nlc[1 + i];
-                       ((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0;
-                       i += ((T30_INFO *)&nlc[1])->head_line_len;
-                       while (i < nlc[0])
-                               plci->fax_connect_info_buffer[len++] = nlc[++i];
-                       plci->fax_connect_info_length = len;
-               }
-               else
-               {
-                       nlc[0] = 14;
-                       if (b3_config->length != 16)
-                               return _B3_PARM_NOT_SUPPORTED;
-                       for (i = 0; i < 12; i++) nlc[1 + i] = b3_config->info[1 + i];
-                       if (GET_WORD(&b3_config->info[13]) != 8 && GET_WORD(&b3_config->info[13]) != 128)
-                               return _B3_PARM_NOT_SUPPORTED;
-                       nlc[13] = b3_config->info[13];
-                       if (GET_WORD(&b3_config->info[15]) >= nlc[13])
-                               return _B3_PARM_NOT_SUPPORTED;
-                       nlc[14] = b3_config->info[15];
-               }
-       }
-       else
-       {
-               if (plci->B3_prot == 4
-                   || plci->B3_prot == 5 /*T.30 - FAX*/) return _B3_PARM_NOT_SUPPORTED;
-       }
-       add_p(plci, NLC, nlc);
-       return 0;
-}
-
-/*----------------------------------------------------------------*/
-/*      make the same as add_b23, but only for the modem related  */
-/*      L2 and L3 B-Chan protocol.                                */
-/*                                                                */
-/*      Enabled L2 and L3 Configurations:                         */
-/*        If L1 == Modem all negotiation                          */
-/*          only L2 == Modem with full negotiation is allowed     */
-/*        If L1 == Modem async or sync                            */
-/*          only L2 == Transparent is allowed                     */
-/*        L3 == Modem or L3 == Transparent are allowed            */
-/*      B2 Configuration for modem:                               */
-/*          word : enable/disable compression, bitoptions         */
-/*      B3 Configuration for modem:                               */
-/*          empty                                                 */
-/*----------------------------------------------------------------*/
-static word add_modem_b23(PLCI *plci, API_PARSE *bp_parms)
-{
-       static byte lli[12] = {1,1};
-       static byte llc[3] = {2,0,0};
-       static byte dlc[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-       API_PARSE mdm_config[2];
-       word i;
-       word b2_config = 0;
-
-       for (i = 0; i < 2; i++) mdm_config[i].length = 0;
-       for (i = 0; i < sizeof(dlc); i++) dlc[i] = 0;
-
-       if (((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
-            && (GET_WORD(bp_parms[1].info) != B2_MODEM_EC_COMPRESSION))
-           || ((GET_WORD(bp_parms[0].info) != B1_MODEM_ALL_NEGOTIATE)
-               && (GET_WORD(bp_parms[1].info) != B2_TRANSPARENT)))
-       {
-               return (_B_STACK_NOT_SUPPORTED);
-       }
-       if ((GET_WORD(bp_parms[2].info) != B3_MODEM)
-           && (GET_WORD(bp_parms[2].info) != B3_TRANSPARENT))
-       {
-               return (_B_STACK_NOT_SUPPORTED);
-       }
-
-       plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
-       plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
-
-       if ((GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) && bp_parms[4].length)
-       {
-               if (api_parse(&bp_parms[4].info[1],
-                             (word)bp_parms[4].length, "w",
-                             mdm_config))
-               {
-                       return (_WRONG_MESSAGE_FORMAT);
-               }
-               b2_config = GET_WORD(mdm_config[0].info);
-       }
-
-       /* OK, L2 is modem */
-
-       lli[0] = 1;
-       lli[1] = 1;
-       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
-               lli[1] |= 2;
-       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
-               lli[1] |= 4;
-
-       if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
-               lli[1] |= 0x10;
-               if (plci->rx_dma_descriptor <= 0) {
-                       plci->rx_dma_descriptor = diva_get_dma_descriptor(plci, &plci->rx_dma_magic);
-                       if (plci->rx_dma_descriptor >= 0)
-                               plci->rx_dma_descriptor++;
-               }
-               if (plci->rx_dma_descriptor > 0) {
-                       lli[1] |= 0x40;
-                       lli[0] = 6;
-                       lli[2] = (byte)(plci->rx_dma_descriptor - 1);
-                       lli[3] = (byte)plci->rx_dma_magic;
-                       lli[4] = (byte)(plci->rx_dma_magic >>  8);
-                       lli[5] = (byte)(plci->rx_dma_magic >> 16);
-                       lli[6] = (byte)(plci->rx_dma_magic >> 24);
-               }
-       }
-
-       if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
-               lli[1] |= 0x20;
-       }
-
-       llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
-               /*V42*/ 10 : /*V42_IN*/ 9;
-       llc[2] = 4;                      /* pass L3 always transparent */
-       add_p(plci, LLI, lli);
-       add_p(plci, LLC, llc);
-       i =  1;
-       PUT_WORD(&dlc[i], plci->appl->MaxDataLength);
-       i += 2;
-       if (GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION)
-       {
-               if (bp_parms[4].length)
-               {
-                       dbug(1, dprintf("MDM b2_config=%02x", b2_config));
-                       dlc[i++] = 3; /* Addr A */
-                       dlc[i++] = 1; /* Addr B */
-                       dlc[i++] = 7; /* modulo mode */
-                       dlc[i++] = 7; /* window size */
-                       dlc[i++] = 0; /* XID len Lo  */
-                       dlc[i++] = 0; /* XID len Hi  */
-
-                       if (b2_config & MDM_B2_DISABLE_V42bis)
-                       {
-                               dlc[i] |= DLC_MODEMPROT_DISABLE_V42_V42BIS;
-                       }
-                       if (b2_config & MDM_B2_DISABLE_MNP)
-                       {
-                               dlc[i] |= DLC_MODEMPROT_DISABLE_MNP_MNP5;
-                       }
-                       if (b2_config & MDM_B2_DISABLE_TRANS)
-                       {
-                               dlc[i] |= DLC_MODEMPROT_REQUIRE_PROTOCOL;
-                       }
-                       if (b2_config & MDM_B2_DISABLE_V42)
-                       {
-                               dlc[i] |= DLC_MODEMPROT_DISABLE_V42_DETECT;
-                       }
-                       if (b2_config & MDM_B2_DISABLE_COMP)
-                       {
-                               dlc[i] |= DLC_MODEMPROT_DISABLE_COMPRESSION;
-                       }
-                       i++;
-               }
-       }
-       else
-       {
-               dlc[i++] = 3; /* Addr A */
-               dlc[i++] = 1; /* Addr B */
-               dlc[i++] = 7; /* modulo mode */
-               dlc[i++] = 7; /* window size */
-               dlc[i++] = 0; /* XID len Lo  */
-               dlc[i++] = 0; /* XID len Hi  */
-               dlc[i++] = DLC_MODEMPROT_DISABLE_V42_V42BIS |
-                       DLC_MODEMPROT_DISABLE_MNP_MNP5 |
-                       DLC_MODEMPROT_DISABLE_V42_DETECT |
-                       DLC_MODEMPROT_DISABLE_COMPRESSION;
-       }
-       dlc[0] = (byte)(i - 1);
-/* HexDump ("DLC", sizeof(dlc), &dlc[0]); */
-       add_p(plci, DLC, dlc);
-       return (0);
-}
-
-
-/*------------------------------------------------------------------*/
-/* send a request for the signaling entity                          */
-/*------------------------------------------------------------------*/
-
-static void sig_req(PLCI *plci, byte req, byte Id)
-{
-       if (!plci) return;
-       if (plci->adapter->adapter_disabled) return;
-       dbug(1, dprintf("sig_req(%x)", req));
-       if (req == REMOVE)
-               plci->sig_remove_id = plci->Sig.Id;
-       if (plci->req_in == plci->req_in_start) {
-               plci->req_in += 2;
-               plci->RBuffer[plci->req_in++] = 0;
-       }
-       PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2);
-       plci->RBuffer[plci->req_in++] = Id;   /* sig/nl flag */
-       plci->RBuffer[plci->req_in++] = req;  /* request */
-       plci->RBuffer[plci->req_in++] = 0;    /* channel */
-       plci->req_in_start = plci->req_in;
-}
-
-/*------------------------------------------------------------------*/
-/* send a request for the network layer entity                      */
-/*------------------------------------------------------------------*/
-
-static void nl_req_ncci(PLCI *plci, byte req, byte ncci)
-{
-       if (!plci) return;
-       if (plci->adapter->adapter_disabled) return;
-       dbug(1, dprintf("nl_req %02x %02x %02x", plci->Id, req, ncci));
-       if (req == REMOVE)
-       {
-               plci->nl_remove_id = plci->NL.Id;
-               ncci_remove(plci, 0, (byte)(ncci != 0));
-               ncci = 0;
-       }
-       if (plci->req_in == plci->req_in_start) {
-               plci->req_in += 2;
-               plci->RBuffer[plci->req_in++] = 0;
-       }
-       PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start - 2);
-       plci->RBuffer[plci->req_in++] = 1;    /* sig/nl flag */
-       plci->RBuffer[plci->req_in++] = req;  /* request */
-       plci->RBuffer[plci->req_in++] = plci->adapter->ncci_ch[ncci];   /* channel */
-       plci->req_in_start = plci->req_in;
-}
-
-static void send_req(PLCI *plci)
-{
-       ENTITY *e;
-       word l;
-/*  word i; */
-
-       if (!plci) return;
-       if (plci->adapter->adapter_disabled) return;
-       channel_xmit_xon(plci);
-
-       /* if nothing to do, return */
-       if (plci->req_in == plci->req_out) return;
-       dbug(1, dprintf("send_req(in=%d,out=%d)", plci->req_in, plci->req_out));
-
-       if (plci->nl_req || plci->sig_req) return;
-
-       l = GET_WORD(&plci->RBuffer[plci->req_out]);
-       plci->req_out += 2;
-       plci->XData[0].P = &plci->RBuffer[plci->req_out];
-       plci->req_out += l;
-       if (plci->RBuffer[plci->req_out] == 1)
-       {
-               e = &plci->NL;
-               plci->req_out++;
-               e->Req = plci->nl_req = plci->RBuffer[plci->req_out++];
-               e->ReqCh = plci->RBuffer[plci->req_out++];
-               if (!(e->Id & 0x1f))
-               {
-                       e->Id = NL_ID;
-                       plci->RBuffer[plci->req_out - 4] = CAI;
-                       plci->RBuffer[plci->req_out - 3] = 1;
-                       plci->RBuffer[plci->req_out - 2] = (plci->Sig.Id == 0xff) ? 0 : plci->Sig.Id;
-                       plci->RBuffer[plci->req_out - 1] = 0;
-                       l += 3;
-                       plci->nl_global_req = plci->nl_req;
-               }
-               dbug(1, dprintf("%x:NLREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh));
-       }
-       else
-       {
-               e = &plci->Sig;
-               if (plci->RBuffer[plci->req_out])
-                       e->Id = plci->RBuffer[plci->req_out];
-               plci->req_out++;
-               e->Req = plci->sig_req = plci->RBuffer[plci->req_out++];
-               e->ReqCh = plci->RBuffer[plci->req_out++];
-               if (!(e->Id & 0x1f))
-                       plci->sig_global_req = plci->sig_req;
-               dbug(1, dprintf("%x:SIGREQ(%x:%x:%x)", plci->adapter->Id, e->Id, e->Req, e->ReqCh));
-       }
-       plci->XData[0].PLength = l;
-       e->X = plci->XData;
-       plci->adapter->request(e);
-       dbug(1, dprintf("send_ok"));
-}
-
-static void send_data(PLCI *plci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       DATA_B3_DESC *data;
-       NCCI   *ncci_ptr;
-       word ncci;
-
-       if (!plci->nl_req && plci->ncci_ring_list)
-       {
-               a = plci->adapter;
-               ncci = plci->ncci_ring_list;
-               do
-               {
-                       ncci = a->ncci_next[ncci];
-                       ncci_ptr = &(a->ncci[ncci]);
-                       if (!(a->ncci_ch[ncci]
-                             && (a->ch_flow_control[a->ncci_ch[ncci]] & N_OK_FC_PENDING)))
-                       {
-                               if (ncci_ptr->data_pending)
-                               {
-                                       if ((a->ncci_state[ncci] == CONNECTED)
-                                           || (a->ncci_state[ncci] == INC_ACT_PENDING)
-                                           || (plci->send_disc == ncci))
-                                       {
-                                               data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
-                                               if ((plci->B2_prot == B2_V120_ASYNC)
-                                                   || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
-                                                   || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))
-                                               {
-                                                       plci->NData[1].P = TransmitBufferGet(plci->appl, data->P);
-                                                       plci->NData[1].PLength = data->Length;
-                                                       if (data->Flags & 0x10)
-                                                               plci->NData[0].P = v120_break_header;
-                                                       else
-                                                               plci->NData[0].P = v120_default_header;
-                                                       plci->NData[0].PLength = 1;
-                                                       plci->NL.XNum = 2;
-                                                       plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA);
-                                               }
-                                               else
-                                               {
-                                                       plci->NData[0].P = TransmitBufferGet(plci->appl, data->P);
-                                                       plci->NData[0].PLength = data->Length;
-                                                       if (data->Flags & 0x10)
-                                                               plci->NL.Req = plci->nl_req = (byte)N_UDATA;
-
-                                                       else if ((plci->B3_prot == B3_RTP) && (data->Flags & 0x01))
-                                                               plci->NL.Req = plci->nl_req = (byte)N_BDATA;
-
-                                                       else
-                                                               plci->NL.Req = plci->nl_req = (byte)((data->Flags & 0x07) << 4 | N_DATA);
-                                               }
-                                               plci->NL.X = plci->NData;
-                                               plci->NL.ReqCh = a->ncci_ch[ncci];
-                                               dbug(1, dprintf("%x:DREQ(%x:%x)", a->Id, plci->NL.Id, plci->NL.Req));
-                                               plci->data_sent = true;
-                                               plci->data_sent_ptr = data->P;
-                                               a->request(&plci->NL);
-                                       }
-                                       else {
-                                               cleanup_ncci_data(plci, ncci);
-                                       }
-                               }
-                               else if (plci->send_disc == ncci)
-                               {
-                                       /* dprintf("N_DISC"); */
-                                       plci->NData[0].PLength = 0;
-                                       plci->NL.ReqCh = a->ncci_ch[ncci];
-                                       plci->NL.Req = plci->nl_req = N_DISC;
-                                       a->request(&plci->NL);
-                                       plci->command = _DISCONNECT_B3_R;
-                                       plci->send_disc = 0;
-                               }
-                       }
-               } while (!plci->nl_req && (ncci != plci->ncci_ring_list));
-               plci->ncci_ring_list = ncci;
-       }
-}
-
-static void listen_check(DIVA_CAPI_ADAPTER *a)
-{
-       word i, j;
-       PLCI *plci;
-       byte activnotifiedcalls = 0;
-
-       dbug(1, dprintf("listen_check(%d,%d)", a->listen_active, a->max_listen));
-       if (!remove_started && !a->adapter_disabled)
-       {
-               for (i = 0; i < a->max_plci; i++)
-               {
-                       plci = &(a->plci[i]);
-                       if (plci->notifiedcall) activnotifiedcalls++;
-               }
-               dbug(1, dprintf("listen_check(%d)", activnotifiedcalls));
-
-               for (i = a->listen_active; i < ((word)(a->max_listen + activnotifiedcalls)); i++) {
-                       if ((j = get_plci(a))) {
-                               a->listen_active++;
-                               plci = &a->plci[j - 1];
-                               plci->State = LISTENING;
-
-                               add_p(plci, OAD, "\x01\xfd");
-
-                               add_p(plci, KEY, "\x04\x43\x41\x32\x30");
-
-                               add_p(plci, CAI, "\x01\xc0");
-                               add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                               add_p(plci, LLI, "\x01\xc4");                  /* support Dummy CR FAC + MWI + SpoofNotify */
-                               add_p(plci, SHIFT | 6, NULL);
-                               add_p(plci, SIN, "\x02\x00\x00");
-                               plci->internal_command = LISTEN_SIG_ASSIGN_PEND;     /* do indicate_req if OK  */
-                               sig_req(plci, ASSIGN, DSIG_ID);
-                               send_req(plci);
-                       }
-               }
-       }
-}
-
-/*------------------------------------------------------------------*/
-/* functions for all parameters sent in INDs                        */
-/*------------------------------------------------------------------*/
-
-static void IndParse(PLCI *plci, const word *parms_id, byte **parms, byte multiIEsize)
-{
-       word ploc;            /* points to current location within packet */
-       byte w;
-       byte wlen;
-       byte codeset, lock;
-       byte *in;
-       word i;
-       word code;
-       word mIEindex = 0;
-       ploc = 0;
-       codeset = 0;
-       lock = 0;
-
-       in = plci->Sig.RBuffer->P;
-       for (i = 0; i < parms_id[0]; i++)   /* multiIE parms_id contains just the 1st */
-       {                            /* element but parms array is larger      */
-               parms[i] = (byte *)"";
-       }
-       for (i = 0; i < multiIEsize; i++)
-       {
-               parms[i] = (byte *)"";
-       }
-
-       while (ploc < plci->Sig.RBuffer->length - 1) {
-
-               /* read information element id and length                   */
-               w = in[ploc];
-
-               if (w & 0x80) {
-/*    w &=0xf0; removed, cannot detect congestion levels */
-/*    upper 4 bit masked with w==SHIFT now               */
-                       wlen = 0;
-               }
-               else {
-                       wlen = (byte)(in[ploc + 1] + 1);
-               }
-               /* check if length valid (not exceeding end of packet)      */
-               if ((ploc + wlen) > 270) return;
-               if (lock & 0x80) lock &= 0x7f;
-               else codeset = lock;
-
-               if ((w & 0xf0) == SHIFT) {
-                       codeset = in[ploc];
-                       if (!(codeset & 0x08)) lock = (byte)(codeset & 7);
-                       codeset &= 7;
-                       lock |= 0x80;
-               }
-               else {
-                       if (w == ESC && wlen >= 3) code = in[ploc + 2] | 0x800;
-                       else code = w;
-                       code |= (codeset << 8);
-
-                       for (i = 1; i < parms_id[0] + 1 && parms_id[i] != code; i++);
-
-                       if (i < parms_id[0] + 1) {
-                               if (!multiIEsize) { /* with multiIEs use next field index,          */
-                                       mIEindex = i - 1;    /* with normal IEs use same index like parms_id */
-                               }
-
-                               parms[mIEindex] = &in[ploc + 1];
-                               dbug(1, dprintf("mIE[%d]=0x%x", *parms[mIEindex], in[ploc]));
-                               if (parms_id[i] == OAD
-                                   || parms_id[i] == CONN_NR
-                                   || parms_id[i] == CAD) {
-                                       if (in[ploc + 2] & 0x80) {
-                                               in[ploc + 0] = (byte)(in[ploc + 1] + 1);
-                                               in[ploc + 1] = (byte)(in[ploc + 2] & 0x7f);
-                                               in[ploc + 2] = 0x80;
-                                               parms[mIEindex] = &in[ploc];
-                                       }
-                               }
-                               mIEindex++;       /* effects multiIEs only */
-                       }
-               }
-
-               ploc += (wlen + 1);
-       }
-       return;
-}
-
-/*------------------------------------------------------------------*/
-/* try to match a cip from received BC and HLC                      */
-/*------------------------------------------------------------------*/
-
-static byte ie_compare(byte *ie1, byte *ie2)
-{
-       word i;
-       if (!ie1 || !ie2) return false;
-       if (!ie1[0]) return false;
-       for (i = 0; i < (word)(ie1[0] + 1); i++) if (ie1[i] != ie2[i]) return false;
-       return true;
-}
-
-static word find_cip(DIVA_CAPI_ADAPTER *a, byte *bc, byte *hlc)
-{
-       word i;
-       word j;
-
-       for (i = 9; i && !ie_compare(bc, cip_bc[i][a->u_law]); i--);
-
-       for (j = 16; j < 29 &&
-                    (!ie_compare(bc, cip_bc[j][a->u_law]) || !ie_compare(hlc, cip_hlc[j])); j++);
-       if (j == 29) return i;
-       return j;
-}
-
-
-static byte AddInfo(byte **add_i,
-                   byte **fty_i,
-                   byte *esc_chi,
-                   byte *facility)
-{
-       byte i;
-       byte j;
-       byte k;
-       byte flen;
-       byte len = 0;
-       /* facility is a nested structure */
-       /* FTY can be more than once      */
-
-       if (esc_chi[0] && !(esc_chi[esc_chi[0]] & 0x7f))
-       {
-               add_i[0] = (byte *)"\x02\x02\x00"; /* use neither b nor d channel */
-       }
-
-       else
-       {
-               add_i[0] = (byte *)"";
-       }
-       if (!fty_i[0][0])
-       {
-               add_i[3] = (byte *)"";
-       }
-       else
-       {    /* facility array found  */
-               for (i = 0, j = 1; i < MAX_MULTI_IE && fty_i[i][0]; i++)
-               {
-                       dbug(1, dprintf("AddIFac[%d]", fty_i[i][0]));
-                       len += fty_i[i][0];
-                       len += 2;
-                       flen = fty_i[i][0];
-                       facility[j++] = 0x1c; /* copy fac IE */
-                       for (k = 0; k <= flen; k++, j++)
-                       {
-                               facility[j] = fty_i[i][k];
-/*      dbug(1, dprintf("%x ",facility[j])); */
-                       }
-               }
-               facility[0] = len;
-               add_i[3] = facility;
-       }
-/*  dbug(1, dprintf("FacArrLen=%d ",len)); */
-       len = add_i[0][0] + add_i[1][0] + add_i[2][0] + add_i[3][0];
-       len += 4;                          /* calculate length of all */
-       return (len);
-}
-
-/*------------------------------------------------------------------*/
-/* voice and codec features                                         */
-/*------------------------------------------------------------------*/
-
-static void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER *a)
-{
-       byte voice_chi[] = "\x02\x18\x01";
-       byte channel;
-
-       channel = chi[chi[0]] & 0x3;
-       dbug(1, dprintf("ExtDevON(Ch=0x%x)", channel));
-       voice_chi[2] = (channel) ? channel : 1;
-       add_p(plci, FTY, "\x02\x01\x07");             /* B On, default on 1 */
-       add_p(plci, ESC, voice_chi);                  /* Channel */
-       sig_req(plci, TEL_CTRL, 0);
-       send_req(plci);
-       if (a->AdvSignalPLCI)
-       {
-               adv_voice_write_coefs(a->AdvSignalPLCI, ADV_VOICE_WRITE_ACTIVATION);
-       }
-}
-
-static void VoiceChannelOff(PLCI *plci)
-{
-       dbug(1, dprintf("ExtDevOFF"));
-       add_p(plci, FTY, "\x02\x01\x08");             /* B Off */
-       sig_req(plci, TEL_CTRL, 0);
-       send_req(plci);
-       if (plci->adapter->AdvSignalPLCI)
-       {
-               adv_voice_clear_config(plci->adapter->AdvSignalPLCI);
-       }
-}
-
-
-static word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl,
-                           byte hook_listen)
-{
-       word j;
-       PLCI *splci;
-
-       /* check if hardware supports handset with hook states (adv.codec) */
-       /* or if just a on board codec is supported                        */
-       /* the advanced codec plci is just for internal use                */
-
-       /* diva Pro with on-board codec:                                   */
-       if (a->profile.Global_Options & HANDSET)
-       {
-               /* new call, but hook states are already signalled */
-               if (a->AdvCodecFLAG)
-               {
-                       if (a->AdvSignalAppl != appl || a->AdvSignalPLCI)
-                       {
-                               dbug(1, dprintf("AdvSigPlci=0x%x", a->AdvSignalPLCI));
-                               return 0x2001; /* codec in use by another application */
-                       }
-                       if (plci != NULL)
-                       {
-                               a->AdvSignalPLCI = plci;
-                               plci->tel = ADV_VOICE;
-                       }
-                       return 0;                      /* adv codec still used */
-               }
-               if ((j = get_plci(a)))
-               {
-                       splci = &a->plci[j - 1];
-                       splci->tel = CODEC_PERMANENT;
-                       /* hook_listen indicates if a facility_req with handset/hook support */
-                       /* was sent. Otherwise if just a call on an external device was made */
-                       /* the codec will be used but the hook info will be discarded (just  */
-                       /* the external controller is in use                                 */
-                       if (hook_listen) splci->State = ADVANCED_VOICE_SIG;
-                       else
-                       {
-                               splci->State = ADVANCED_VOICE_NOSIG;
-                               if (plci)
-                               {
-                                       plci->spoofed_msg = SPOOFING_REQUIRED;
-                               }
-                               /* indicate D-ch connect if  */
-                       }                                        /* codec is connected OK     */
-                       if (plci != NULL)
-                       {
-                               a->AdvSignalPLCI = plci;
-                               plci->tel = ADV_VOICE;
-                       }
-                       a->AdvSignalAppl = appl;
-                       a->AdvCodecFLAG = true;
-                       a->AdvCodecPLCI = splci;
-                       add_p(splci, CAI, "\x01\x15");
-                       add_p(splci, LLI, "\x01\x00");
-                       add_p(splci, ESC, "\x02\x18\x00");
-                       add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                       splci->internal_command = PERM_COD_ASSIGN;
-                       dbug(1, dprintf("Codec Assign"));
-                       sig_req(splci, ASSIGN, DSIG_ID);
-                       send_req(splci);
-               }
-               else
-               {
-                       return 0x2001; /* wrong state, no more plcis */
-               }
-       }
-       else if (a->profile.Global_Options & ON_BOARD_CODEC)
-       {
-               if (hook_listen) return 0x300B;               /* Facility not supported */
-               /* no hook with SCOM      */
-               if (plci != NULL) plci->tel = CODEC;
-               dbug(1, dprintf("S/SCOM codec"));
-               /* first time we use the scom-s codec we must shut down the internal   */
-               /* handset application of the card. This can be done by an assign with */
-               /* a cai with the 0x80 bit set. Assign return code is 'out of resource'*/
-               if (!a->scom_appl_disable) {
-                       if ((j = get_plci(a))) {
-                               splci = &a->plci[j - 1];
-                               add_p(splci, CAI, "\x01\x80");
-                               add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                               sig_req(splci, ASSIGN, 0xC0);  /* 0xc0 is the TEL_ID */
-                               send_req(splci);
-                               a->scom_appl_disable = true;
-                       }
-                       else{
-                               return 0x2001; /* wrong state, no more plcis */
-                       }
-               }
-       }
-       else return 0x300B;               /* Facility not supported */
-
-       return 0;
-}
-
-
-static void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci)
-{
-
-       dbug(1, dprintf("CodecIdCheck"));
-
-       if (a->AdvSignalPLCI == plci)
-       {
-               dbug(1, dprintf("PLCI owns codec"));
-               VoiceChannelOff(a->AdvCodecPLCI);
-               if (a->AdvCodecPLCI->State == ADVANCED_VOICE_NOSIG)
-               {
-                       dbug(1, dprintf("remove temp codec PLCI"));
-                       plci_remove(a->AdvCodecPLCI);
-                       a->AdvCodecFLAG  = 0;
-                       a->AdvCodecPLCI  = NULL;
-                       a->AdvSignalAppl = NULL;
-               }
-               a->AdvSignalPLCI = NULL;
-       }
-}
-
-/* -------------------------------------------------------------------
-   Ask for physical address of card on PCI bus
-   ------------------------------------------------------------------- */
-static void diva_ask_for_xdi_sdram_bar(DIVA_CAPI_ADAPTER *a,
-                                      IDI_SYNC_REQ *preq) {
-       a->sdram_bar = 0;
-       if (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR) {
-               ENTITY *e = (ENTITY *)preq;
-
-               e->user[0] = a->Id - 1;
-               preq->xdi_sdram_bar.info.bar    = 0;
-               preq->xdi_sdram_bar.Req         = 0;
-               preq->xdi_sdram_bar.Rc           = IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR;
-
-               (*(a->request))(e);
-
-               a->sdram_bar = preq->xdi_sdram_bar.info.bar;
-               dbug(3, dprintf("A(%d) SDRAM BAR = %08x", a->Id, a->sdram_bar));
-       }
-}
-
-/* -------------------------------------------------------------------
-   Ask XDI about extended features
-   ------------------------------------------------------------------- */
-static void diva_get_extended_adapter_features(DIVA_CAPI_ADAPTER *a) {
-       IDI_SYNC_REQ *preq;
-       char buffer[((sizeof(preq->xdi_extended_features) + 4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features) + 4) : sizeof(ENTITY)];
-
-       char features[4];
-       preq = (IDI_SYNC_REQ *)&buffer[0];
-
-       if (!diva_xdi_extended_features) {
-               ENTITY *e = (ENTITY *)preq;
-               diva_xdi_extended_features |= 0x80000000;
-
-               e->user[0] = a->Id - 1;
-               preq->xdi_extended_features.Req = 0;
-               preq->xdi_extended_features.Rc  = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
-               preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
-               preq->xdi_extended_features.info.features = &features[0];
-
-               (*(a->request))(e);
-
-               if (features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) {
-                       /*
-                         Check features located in the byte '0'
-                       */
-                       if (features[0] & DIVA_XDI_EXTENDED_FEATURE_CMA) {
-                               diva_xdi_extended_features |= DIVA_CAPI_USE_CMA;
-                       }
-                       if (features[0] & DIVA_XDI_EXTENDED_FEATURE_RX_DMA) {
-                               diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_RX_DMA;
-                               dbug(1, dprintf("XDI provides RxDMA"));
-                       }
-                       if (features[0] & DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR) {
-                               diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR;
-                       }
-                       if (features[0] & DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC) {
-                               diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_NO_CANCEL;
-                               dbug(3, dprintf("XDI provides NO_CANCEL_RC feature"));
-                       }
-
-               }
-       }
-
-       diva_ask_for_xdi_sdram_bar(a, preq);
-}
-
-/*------------------------------------------------------------------*/
-/* automatic law                                                    */
-/*------------------------------------------------------------------*/
-/* called from OS specific part after init time to get the Law              */
-/* a-law (Euro) and u-law (us,japan) use different BCs in the Setup message */
-void AutomaticLaw(DIVA_CAPI_ADAPTER *a)
-{
-       word j;
-       PLCI *splci;
-
-       if (a->automatic_law) {
-               return;
-       }
-       if ((j = get_plci(a))) {
-               diva_get_extended_adapter_features(a);
-               splci = &a->plci[j - 1];
-               a->automatic_lawPLCI = splci;
-               a->automatic_law = 1;
-               add_p(splci, CAI, "\x01\x80");
-               add_p(splci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-               splci->internal_command = USELAW_REQ;
-               splci->command = 0;
-               splci->number = 0;
-               sig_req(splci, ASSIGN, DSIG_ID);
-               send_req(splci);
-       }
-}
-
-/* called from OS specific part if an application sends an Capi20Release */
-word CapiRelease(word Id)
-{
-       word i, j, appls_found;
-       PLCI *plci;
-       APPL   *this;
-       DIVA_CAPI_ADAPTER *a;
-
-       if (!Id)
-       {
-               dbug(0, dprintf("A: CapiRelease(Id==0)"));
-               return (_WRONG_APPL_ID);
-       }
-
-       this = &application[Id - 1];               /* get application pointer */
-
-       for (i = 0, appls_found = 0; i < max_appl; i++)
-       {
-               if (application[i].Id)       /* an application has been found        */
-               {
-                       appls_found++;
-               }
-       }
-
-       for (i = 0; i < max_adapter; i++)             /* scan all adapters...    */
-       {
-               a = &adapter[i];
-               if (a->request)
-               {
-                       a->Info_Mask[Id - 1] = 0;
-                       a->CIP_Mask[Id - 1] = 0;
-                       a->Notification_Mask[Id - 1] = 0;
-                       a->codec_listen[Id - 1] = NULL;
-                       a->requested_options_table[Id - 1] = 0;
-                       for (j = 0; j < a->max_plci; j++)           /* and all PLCIs connected */
-                       {                                      /* with this application   */
-                               plci = &a->plci[j];
-                               if (plci->Id)                         /* if plci owns no application */
-                               {                                    /* it may be not jet connected */
-                                       if (plci->State == INC_CON_PENDING
-                                           || plci->State == INC_CON_ALERT)
-                                       {
-                                               if (test_bit(Id - 1, plci->c_ind_mask_table))
-                                               {
-                                                       __clear_bit(Id - 1, plci->c_ind_mask_table);
-                                                       if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL))
-                                                       {
-                                                               sig_req(plci, HANGUP, 0);
-                                                               send_req(plci);
-                                                               plci->State = OUTG_DIS_PENDING;
-                                                       }
-                                               }
-                                       }
-                                       if (test_bit(Id - 1, plci->c_ind_mask_table))
-                                       {
-                                               __clear_bit(Id - 1, plci->c_ind_mask_table);
-                                               if (bitmap_empty(plci->c_ind_mask_table, MAX_APPL))
-                                               {
-                                                       if (!plci->appl)
-                                                       {
-                                                               plci_remove(plci);
-                                                               plci->State = IDLE;
-                                                       }
-                                               }
-                                       }
-                                       if (plci->appl == this)
-                                       {
-                                               plci->appl = NULL;
-                                               plci_remove(plci);
-                                               plci->State = IDLE;
-                                       }
-                               }
-                       }
-                       listen_check(a);
-
-                       if (a->flag_dynamic_l1_down)
-                       {
-                               if (appls_found == 1)            /* last application does a capi release */
-                               {
-                                       if ((j = get_plci(a)))
-                                       {
-                                               plci = &a->plci[j - 1];
-                                               plci->command = 0;
-                                               add_p(plci, OAD, "\x01\xfd");
-                                               add_p(plci, CAI, "\x01\x80");
-                                               add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               add_p(plci, SHIFT | 6, NULL);
-                                               add_p(plci, SIN, "\x02\x00\x00");
-                                               plci->internal_command = REM_L1_SIG_ASSIGN_PEND;
-                                               sig_req(plci, ASSIGN, DSIG_ID);
-                                               add_p(plci, FTY, "\x02\xff\x06"); /* l1 down */
-                                               sig_req(plci, SIG_CTRL, 0);
-                                               send_req(plci);
-                                       }
-                               }
-                       }
-                       if (a->AdvSignalAppl == this)
-                       {
-                               this->NullCREnable = false;
-                               if (a->AdvCodecPLCI)
-                               {
-                                       plci_remove(a->AdvCodecPLCI);
-                                       a->AdvCodecPLCI->tel = 0;
-                                       a->AdvCodecPLCI->adv_nl = 0;
-                               }
-                               a->AdvSignalAppl = NULL;
-                               a->AdvSignalPLCI = NULL;
-                               a->AdvCodecFLAG = 0;
-                               a->AdvCodecPLCI = NULL;
-                       }
-               }
-       }
-
-       this->Id = 0;
-
-       return GOOD;
-}
-
-static word plci_remove_check(PLCI *plci)
-{
-       if (!plci) return true;
-       if (!plci->NL.Id && bitmap_empty(plci->c_ind_mask_table, MAX_APPL))
-       {
-               if (plci->Sig.Id == 0xff)
-                       plci->Sig.Id = 0;
-               if (!plci->Sig.Id)
-               {
-                       dbug(1, dprintf("plci_remove_complete(%x)", plci->Id));
-                       dbug(1, dprintf("tel=0x%x,Sig=0x%x", plci->tel, plci->Sig.Id));
-                       if (plci->Id)
-                       {
-                               CodecIdCheck(plci->adapter, plci);
-                               clear_b1_config(plci);
-                               ncci_remove(plci, 0, false);
-                               plci_free_msg_in_queue(plci);
-                               channel_flow_control_remove(plci);
-                               plci->Id = 0;
-                               plci->State = IDLE;
-                               plci->channels = 0;
-                               plci->appl = NULL;
-                               plci->notifiedcall = 0;
-                       }
-                       listen_check(plci->adapter);
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-/*------------------------------------------------------------------*/
-
-static byte plci_nl_busy(PLCI *plci)
-{
-       /* only applicable for non-multiplexed protocols */
-       return (plci->nl_req
-               || (plci->ncci_ring_list
-                   && plci->adapter->ncci_ch[plci->ncci_ring_list]
-                   && (plci->adapter->ch_flow_control[plci->adapter->ncci_ch[plci->ncci_ring_list]] & N_OK_FC_PENDING)));
-}
-
-
-/*------------------------------------------------------------------*/
-/* DTMF facilities                                                  */
-/*------------------------------------------------------------------*/
-
-
-static struct
-{
-       byte send_mask;
-       byte listen_mask;
-       byte character;
-       byte code;
-} dtmf_digit_map[] =
-{
-       { 0x01, 0x01, 0x23, DTMF_DIGIT_TONE_CODE_HASHMARK },
-       { 0x01, 0x01, 0x2a, DTMF_DIGIT_TONE_CODE_STAR },
-       { 0x01, 0x01, 0x30, DTMF_DIGIT_TONE_CODE_0 },
-       { 0x01, 0x01, 0x31, DTMF_DIGIT_TONE_CODE_1 },
-       { 0x01, 0x01, 0x32, DTMF_DIGIT_TONE_CODE_2 },
-       { 0x01, 0x01, 0x33, DTMF_DIGIT_TONE_CODE_3 },
-       { 0x01, 0x01, 0x34, DTMF_DIGIT_TONE_CODE_4 },
-       { 0x01, 0x01, 0x35, DTMF_DIGIT_TONE_CODE_5 },
-       { 0x01, 0x01, 0x36, DTMF_DIGIT_TONE_CODE_6 },
-       { 0x01, 0x01, 0x37, DTMF_DIGIT_TONE_CODE_7 },
-       { 0x01, 0x01, 0x38, DTMF_DIGIT_TONE_CODE_8 },
-       { 0x01, 0x01, 0x39, DTMF_DIGIT_TONE_CODE_9 },
-       { 0x01, 0x01, 0x41, DTMF_DIGIT_TONE_CODE_A },
-       { 0x01, 0x01, 0x42, DTMF_DIGIT_TONE_CODE_B },
-       { 0x01, 0x01, 0x43, DTMF_DIGIT_TONE_CODE_C },
-       { 0x01, 0x01, 0x44, DTMF_DIGIT_TONE_CODE_D },
-       { 0x01, 0x00, 0x61, DTMF_DIGIT_TONE_CODE_A },
-       { 0x01, 0x00, 0x62, DTMF_DIGIT_TONE_CODE_B },
-       { 0x01, 0x00, 0x63, DTMF_DIGIT_TONE_CODE_C },
-       { 0x01, 0x00, 0x64, DTMF_DIGIT_TONE_CODE_D },
-
-       { 0x04, 0x04, 0x80, DTMF_SIGNAL_NO_TONE },
-       { 0x00, 0x04, 0x81, DTMF_SIGNAL_UNIDENTIFIED_TONE },
-       { 0x04, 0x04, 0x82, DTMF_SIGNAL_DIAL_TONE },
-       { 0x04, 0x04, 0x83, DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE },
-       { 0x04, 0x04, 0x84, DTMF_SIGNAL_SPECIAL_DIAL_TONE },
-       { 0x04, 0x04, 0x85, DTMF_SIGNAL_SECOND_DIAL_TONE },
-       { 0x04, 0x04, 0x86, DTMF_SIGNAL_RINGING_TONE },
-       { 0x04, 0x04, 0x87, DTMF_SIGNAL_SPECIAL_RINGING_TONE },
-       { 0x04, 0x04, 0x88, DTMF_SIGNAL_BUSY_TONE },
-       { 0x04, 0x04, 0x89, DTMF_SIGNAL_CONGESTION_TONE },
-       { 0x04, 0x04, 0x8a, DTMF_SIGNAL_SPECIAL_INFORMATION_TONE },
-       { 0x04, 0x04, 0x8b, DTMF_SIGNAL_COMFORT_TONE },
-       { 0x04, 0x04, 0x8c, DTMF_SIGNAL_HOLD_TONE },
-       { 0x04, 0x04, 0x8d, DTMF_SIGNAL_RECORD_TONE },
-       { 0x04, 0x04, 0x8e, DTMF_SIGNAL_CALLER_WAITING_TONE },
-       { 0x04, 0x04, 0x8f, DTMF_SIGNAL_CALL_WAITING_TONE },
-       { 0x04, 0x04, 0x90, DTMF_SIGNAL_PAY_TONE },
-       { 0x04, 0x04, 0x91, DTMF_SIGNAL_POSITIVE_INDICATION_TONE },
-       { 0x04, 0x04, 0x92, DTMF_SIGNAL_NEGATIVE_INDICATION_TONE },
-       { 0x04, 0x04, 0x93, DTMF_SIGNAL_WARNING_TONE },
-       { 0x04, 0x04, 0x94, DTMF_SIGNAL_INTRUSION_TONE },
-       { 0x04, 0x04, 0x95, DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE },
-       { 0x04, 0x04, 0x96, DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE },
-       { 0x04, 0x04, 0x97, DTMF_SIGNAL_CPE_ALERTING_SIGNAL },
-       { 0x04, 0x04, 0x98, DTMF_SIGNAL_OFF_HOOK_WARNING_TONE },
-       { 0x04, 0x04, 0xbf, DTMF_SIGNAL_INTERCEPT_TONE },
-       { 0x04, 0x04, 0xc0, DTMF_SIGNAL_MODEM_CALLING_TONE },
-       { 0x04, 0x04, 0xc1, DTMF_SIGNAL_FAX_CALLING_TONE },
-       { 0x04, 0x04, 0xc2, DTMF_SIGNAL_ANSWER_TONE },
-       { 0x04, 0x04, 0xc3, DTMF_SIGNAL_REVERSED_ANSWER_TONE },
-       { 0x04, 0x04, 0xc4, DTMF_SIGNAL_ANSAM_TONE },
-       { 0x04, 0x04, 0xc5, DTMF_SIGNAL_REVERSED_ANSAM_TONE },
-       { 0x04, 0x04, 0xc6, DTMF_SIGNAL_BELL103_ANSWER_TONE },
-       { 0x04, 0x04, 0xc7, DTMF_SIGNAL_FAX_FLAGS },
-       { 0x04, 0x04, 0xc8, DTMF_SIGNAL_G2_FAX_GROUP_ID },
-       { 0x00, 0x04, 0xc9, DTMF_SIGNAL_HUMAN_SPEECH },
-       { 0x04, 0x04, 0xca, DTMF_SIGNAL_ANSWERING_MACHINE_390 },
-       { 0x02, 0x02, 0xf1, DTMF_MF_DIGIT_TONE_CODE_1 },
-       { 0x02, 0x02, 0xf2, DTMF_MF_DIGIT_TONE_CODE_2 },
-       { 0x02, 0x02, 0xf3, DTMF_MF_DIGIT_TONE_CODE_3 },
-       { 0x02, 0x02, 0xf4, DTMF_MF_DIGIT_TONE_CODE_4 },
-       { 0x02, 0x02, 0xf5, DTMF_MF_DIGIT_TONE_CODE_5 },
-       { 0x02, 0x02, 0xf6, DTMF_MF_DIGIT_TONE_CODE_6 },
-       { 0x02, 0x02, 0xf7, DTMF_MF_DIGIT_TONE_CODE_7 },
-       { 0x02, 0x02, 0xf8, DTMF_MF_DIGIT_TONE_CODE_8 },
-       { 0x02, 0x02, 0xf9, DTMF_MF_DIGIT_TONE_CODE_9 },
-       { 0x02, 0x02, 0xfa, DTMF_MF_DIGIT_TONE_CODE_0 },
-       { 0x02, 0x02, 0xfb, DTMF_MF_DIGIT_TONE_CODE_K1 },
-       { 0x02, 0x02, 0xfc, DTMF_MF_DIGIT_TONE_CODE_K2 },
-       { 0x02, 0x02, 0xfd, DTMF_MF_DIGIT_TONE_CODE_KP },
-       { 0x02, 0x02, 0xfe, DTMF_MF_DIGIT_TONE_CODE_S1 },
-       { 0x02, 0x02, 0xff, DTMF_MF_DIGIT_TONE_CODE_ST },
-
-};
-
-#define DTMF_DIGIT_MAP_ENTRIES ARRAY_SIZE(dtmf_digit_map)
-
-
-static void dtmf_enable_receiver(PLCI *plci, byte enable_mask)
-{
-       word min_digit_duration, min_gap_duration;
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_enable_receiver %02x",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, enable_mask));
-
-       if (enable_mask != 0)
-       {
-               min_digit_duration = (plci->dtmf_rec_pulse_ms == 0) ? 40 : plci->dtmf_rec_pulse_ms;
-               min_gap_duration = (plci->dtmf_rec_pause_ms == 0) ? 40 : plci->dtmf_rec_pause_ms;
-               plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_ENABLE_RECEIVER;
-               PUT_WORD(&plci->internal_req_buffer[1], min_digit_duration);
-               PUT_WORD(&plci->internal_req_buffer[3], min_gap_duration);
-               plci->NData[0].PLength = 5;
-
-               PUT_WORD(&plci->internal_req_buffer[5], INTERNAL_IND_BUFFER_SIZE);
-               plci->NData[0].PLength += 2;
-               capidtmf_recv_enable(&(plci->capidtmf_state), min_digit_duration, min_gap_duration);
-
-       }
-       else
-       {
-               plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_DISABLE_RECEIVER;
-               plci->NData[0].PLength = 1;
-
-               capidtmf_recv_disable(&(plci->capidtmf_state));
-
-       }
-       plci->NData[0].P = plci->internal_req_buffer;
-       plci->NL.X = plci->NData;
-       plci->NL.ReqCh = 0;
-       plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-       plci->adapter->request(&plci->NL);
-}
-
-
-static void dtmf_send_digits(PLCI *plci, byte *digit_buffer, word digit_count)
-{
-       word w, i;
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_digits %d",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, digit_count));
-
-       plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_SEND_DIGITS;
-       w = (plci->dtmf_send_pulse_ms == 0) ? 40 : plci->dtmf_send_pulse_ms;
-       PUT_WORD(&plci->internal_req_buffer[1], w);
-       w = (plci->dtmf_send_pause_ms == 0) ? 40 : plci->dtmf_send_pause_ms;
-       PUT_WORD(&plci->internal_req_buffer[3], w);
-       for (i = 0; i < digit_count; i++)
-       {
-               w = 0;
-               while ((w < DTMF_DIGIT_MAP_ENTRIES)
-                      && (digit_buffer[i] != dtmf_digit_map[w].character))
-               {
-                       w++;
-               }
-               plci->internal_req_buffer[5 + i] = (w < DTMF_DIGIT_MAP_ENTRIES) ?
-                       dtmf_digit_map[w].code : DTMF_DIGIT_TONE_CODE_STAR;
-       }
-       plci->NData[0].PLength = 5 + digit_count;
-       plci->NData[0].P = plci->internal_req_buffer;
-       plci->NL.X = plci->NData;
-       plci->NL.ReqCh = 0;
-       plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-       plci->adapter->request(&plci->NL);
-}
-
-
-static void dtmf_rec_clear_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_rec_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->dtmf_rec_active = 0;
-       plci->dtmf_rec_pulse_ms = 0;
-       plci->dtmf_rec_pause_ms = 0;
-
-       capidtmf_init(&(plci->capidtmf_state), plci->adapter->u_law);
-
-}
-
-
-static void dtmf_send_clear_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_send_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->dtmf_send_requests = 0;
-       plci->dtmf_send_pulse_ms = 0;
-       plci->dtmf_send_pause_ms = 0;
-}
-
-
-static void dtmf_prepare_switch(dword Id, PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_prepare_switch",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       while (plci->dtmf_send_requests != 0)
-               dtmf_confirmation(Id, plci);
-}
-
-
-static word dtmf_save_config(dword Id, PLCI *plci, byte Rc)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_save_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       return (GOOD);
-}
-
-
-static word dtmf_restore_config(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_restore_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       if (plci->B1_facilities & B1_FACILITY_DTMFR)
-       {
-               switch (plci->adjust_b_state)
-               {
-               case ADJUST_B_RESTORE_DTMF_1:
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci_nl_busy(plci))
-                       {
-                               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
-                               break;
-                       }
-                       dtmf_enable_receiver(plci, plci->dtmf_rec_active);
-                       plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_2;
-                       break;
-               case ADJUST_B_RESTORE_DTMF_2:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Reenable DTMF receiver failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-                       break;
-               }
-       }
-       return (Info);
-}
-
-
-static void dtmf_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command, Info;
-       byte mask;
-       byte result[4];
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_command %02x %04x %04x %d %d %d %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
-                       plci->dtmf_cmd, plci->dtmf_rec_pulse_ms, plci->dtmf_rec_pause_ms,
-                       plci->dtmf_send_pulse_ms, plci->dtmf_send_pause_ms));
-
-       Info = GOOD;
-       result[0] = 2;
-       PUT_WORD(&result[1], DTMF_SUCCESS);
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       mask = 0x01;
-       switch (plci->dtmf_cmd)
-       {
-
-       case DTMF_LISTEN_TONE_START:
-               mask <<= 1; /* fall through */
-       case DTMF_LISTEN_MF_START:
-               mask <<= 1; /* fall through */
-
-       case DTMF_LISTEN_START:
-               switch (internal_command)
-               {
-               default:
-                       adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
-                                                                 B1_FACILITY_DTMFR), DTMF_COMMAND_1);
-                       /* fall through */
-               case DTMF_COMMAND_1:
-                       if (adjust_b_process(Id, plci, Rc) != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               return;
-                       /* fall through */
-               case DTMF_COMMAND_2:
-                       if (plci_nl_busy(plci))
-                       {
-                               plci->internal_command = DTMF_COMMAND_2;
-                               return;
-                       }
-                       plci->internal_command = DTMF_COMMAND_3;
-                       dtmf_enable_receiver(plci, (byte)(plci->dtmf_rec_active | mask));
-                       return;
-               case DTMF_COMMAND_3:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Enable DTMF receiver failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-
-                       plci->tone_last_indication_code = DTMF_SIGNAL_NO_TONE;
-
-                       plci->dtmf_rec_active |= mask;
-                       break;
-               }
-               break;
-
-
-       case DTMF_LISTEN_TONE_STOP:
-               mask <<= 1; /* fall through */
-       case DTMF_LISTEN_MF_STOP:
-               mask <<= 1; /* fall through */
-
-       case DTMF_LISTEN_STOP:
-               switch (internal_command)
-               {
-               default:
-                       plci->dtmf_rec_active &= ~mask;
-                       if (plci->dtmf_rec_active)
-                               break;
-/*
-  case DTMF_COMMAND_1:
-  if (plci->dtmf_rec_active)
-  {
-  if (plci_nl_busy (plci))
-  {
-  plci->internal_command = DTMF_COMMAND_1;
-  return;
-  }
-  plci->dtmf_rec_active &= ~mask;
-  plci->internal_command = DTMF_COMMAND_2;
-  dtmf_enable_receiver (plci, false);
-  return;
-  }
-  Rc = OK;
-  case DTMF_COMMAND_2:
-  if ((Rc != OK) && (Rc != OK_FC))
-  {
-  dbug (1, dprintf("[%06lx] %s,%d: Disable DTMF receiver failed %02x",
-  UnMapId (Id), (char far *)(FILE_), __LINE__, Rc));
-  Info = _FACILITY_NOT_SUPPORTED;
-  break;
-  }
-*/
-                       adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
-                                                                 ~(B1_FACILITY_DTMFX | B1_FACILITY_DTMFR)), DTMF_COMMAND_3);
-                       /* fall through */
-               case DTMF_COMMAND_3:
-                       if (adjust_b_process(Id, plci, Rc) != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Unload DTMF failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               return;
-                       break;
-               }
-               break;
-
-
-       case DTMF_SEND_TONE:
-               mask <<= 1; /* fall through */
-       case DTMF_SEND_MF:
-               mask <<= 1; /* fall through */
-
-       case DTMF_DIGITS_SEND:
-               switch (internal_command)
-               {
-               default:
-                       adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
-                                                                 ((plci->dtmf_parameter_length != 0) ? B1_FACILITY_DTMFX | B1_FACILITY_DTMFR : B1_FACILITY_DTMFX)),
-                                          DTMF_COMMAND_1);
-                       /* fall through */
-               case DTMF_COMMAND_1:
-                       if (adjust_b_process(Id, plci, Rc) != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Load DTMF failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               return;
-                       /* fall through */
-               case DTMF_COMMAND_2:
-                       if (plci_nl_busy(plci))
-                       {
-                               plci->internal_command = DTMF_COMMAND_2;
-                               return;
-                       }
-                       plci->dtmf_msg_number_queue[(plci->dtmf_send_requests)++] = plci->number;
-                       plci->internal_command = DTMF_COMMAND_3;
-                       dtmf_send_digits(plci, &plci->saved_msg.parms[3].info[1], plci->saved_msg.parms[3].length);
-                       return;
-               case DTMF_COMMAND_3:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Send DTMF digits failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               if (plci->dtmf_send_requests != 0)
-                                       (plci->dtmf_send_requests)--;
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       return;
-               }
-               break;
-       }
-       sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
-             "wws", Info, SELECTOR_DTMF, result);
-}
-
-
-static byte dtmf_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
-{
-       word Info;
-       word i, j;
-       byte mask;
-       API_PARSE dtmf_parms[5];
-       byte result[40];
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_request",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       Info = GOOD;
-       result[0] = 2;
-       PUT_WORD(&result[1], DTMF_SUCCESS);
-       if (!(a->profile.Global_Options & GL_DTMF_SUPPORTED))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _FACILITY_NOT_SUPPORTED;
-       }
-       else if (api_parse(&msg[1].info[1], msg[1].length, "w", dtmf_parms))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _WRONG_MESSAGE_FORMAT;
-       }
-
-       else if ((GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
-                || (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_SEND_CODES))
-       {
-               if (!((a->requested_options_table[appl->Id - 1])
-                     & (1L << PRIVATE_DTMF_TONE)))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
-                       PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
-               }
-               else
-               {
-                       for (i = 0; i < 32; i++)
-                               result[4 + i] = 0;
-                       if (GET_WORD(dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
-                       {
-                               for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
-                               {
-                                       if (dtmf_digit_map[i].listen_mask != 0)
-                                               result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
-                               }
-                       }
-                       else
-                       {
-                               for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
-                               {
-                                       if (dtmf_digit_map[i].send_mask != 0)
-                                               result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
-                               }
-                       }
-                       result[0] = 3 + 32;
-                       result[3] = 32;
-               }
-       }
-
-       else if (plci == NULL)
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _WRONG_IDENTIFIER;
-       }
-       else
-       {
-               if (!plci->State
-                   || !plci->NL.Id || plci->nl_remove_id)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       Info = _WRONG_STATE;
-               }
-               else
-               {
-                       plci->command = 0;
-                       plci->dtmf_cmd = GET_WORD(dtmf_parms[0].info);
-                       mask = 0x01;
-                       switch (plci->dtmf_cmd)
-                       {
-
-                       case DTMF_LISTEN_TONE_START:
-                       case DTMF_LISTEN_TONE_STOP:
-                               mask <<= 1; /* fall through */
-                       case DTMF_LISTEN_MF_START:
-                       case DTMF_LISTEN_MF_STOP:
-                               mask <<= 1;
-                               if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1])
-                                     & (1L << PRIVATE_DTMF_TONE)))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
-                                       PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
-                                       break;
-                               }
-                               /* fall through */
-
-                       case DTMF_LISTEN_START:
-                       case DTMF_LISTEN_STOP:
-                               if (!(a->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
-                                   && !(a->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _FACILITY_NOT_SUPPORTED;
-                                       break;
-                               }
-                               if (mask & DTMF_LISTEN_ACTIVE_FLAG)
-                               {
-                                       if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
-                                       {
-                                               plci->dtmf_rec_pulse_ms = 0;
-                                               plci->dtmf_rec_pause_ms = 0;
-                                       }
-                                       else
-                                       {
-                                               plci->dtmf_rec_pulse_ms = GET_WORD(dtmf_parms[1].info);
-                                               plci->dtmf_rec_pause_ms = GET_WORD(dtmf_parms[2].info);
-                                       }
-                               }
-                               start_internal_command(Id, plci, dtmf_command);
-                               return (false);
-
-
-                       case DTMF_SEND_TONE:
-                               mask <<= 1; /* fall through */
-                       case DTMF_SEND_MF:
-                               mask <<= 1;
-                               if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id - 1])
-                                     & (1L << PRIVATE_DTMF_TONE)))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(dtmf_parms[0].info)));
-                                       PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
-                                       break;
-                               }
-                               /* fall through */
-
-                       case DTMF_DIGITS_SEND:
-                               if (api_parse(&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               if (mask & DTMF_LISTEN_ACTIVE_FLAG)
-                               {
-                                       plci->dtmf_send_pulse_ms = GET_WORD(dtmf_parms[1].info);
-                                       plci->dtmf_send_pause_ms = GET_WORD(dtmf_parms[2].info);
-                               }
-                               i = 0;
-                               j = 0;
-                               while ((i < dtmf_parms[3].length) && (j < DTMF_DIGIT_MAP_ENTRIES))
-                               {
-                                       j = 0;
-                                       while ((j < DTMF_DIGIT_MAP_ENTRIES)
-                                              && ((dtmf_parms[3].info[i + 1] != dtmf_digit_map[j].character)
-                                                  || ((dtmf_digit_map[j].send_mask & mask) == 0)))
-                                       {
-                                               j++;
-                                       }
-                                       i++;
-                               }
-                               if (j == DTMF_DIGIT_MAP_ENTRIES)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Incorrect DTMF digit %02x",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__, dtmf_parms[3].info[i]));
-                                       PUT_WORD(&result[1], DTMF_INCORRECT_DIGIT);
-                                       break;
-                               }
-                               if (plci->dtmf_send_requests >= ARRAY_SIZE(plci->dtmf_msg_number_queue))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: DTMF request overrun",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_STATE;
-                                       break;
-                               }
-                               api_save_msg(dtmf_parms, "wwws", &plci->saved_msg);
-                               start_internal_command(Id, plci, dtmf_command);
-                               return (false);
-
-                       default:
-                               dbug(1, dprintf("[%06lx] %s,%d: DTMF unknown request %04x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, plci->dtmf_cmd));
-                               PUT_WORD(&result[1], DTMF_UNKNOWN_REQUEST);
-                       }
-               }
-       }
-       sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
-             "wws", Info, SELECTOR_DTMF, result);
-       return (false);
-}
-
-
-static void dtmf_confirmation(dword Id, PLCI *plci)
-{
-       word i;
-       byte result[4];
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_confirmation",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       result[0] = 2;
-       PUT_WORD(&result[1], DTMF_SUCCESS);
-       if (plci->dtmf_send_requests != 0)
-       {
-               sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->dtmf_msg_number_queue[0],
-                     "wws", GOOD, SELECTOR_DTMF, result);
-               (plci->dtmf_send_requests)--;
-               for (i = 0; i < plci->dtmf_send_requests; i++)
-                       plci->dtmf_msg_number_queue[i] = plci->dtmf_msg_number_queue[i + 1];
-       }
-}
-
-
-static void dtmf_indication(dword Id, PLCI *plci, byte *msg, word length)
-{
-       word i, j, n;
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_indication",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       n = 0;
-       for (i = 1; i < length; i++)
-       {
-               j = 0;
-               while ((j < DTMF_DIGIT_MAP_ENTRIES)
-                      && ((msg[i] != dtmf_digit_map[j].code)
-                          || ((dtmf_digit_map[j].listen_mask & plci->dtmf_rec_active) == 0)))
-               {
-                       j++;
-               }
-               if (j < DTMF_DIGIT_MAP_ENTRIES)
-               {
-
-                       if ((dtmf_digit_map[j].listen_mask & DTMF_TONE_LISTEN_ACTIVE_FLAG)
-                           && (plci->tone_last_indication_code == DTMF_SIGNAL_NO_TONE)
-                           && (dtmf_digit_map[j].character != DTMF_SIGNAL_UNIDENTIFIED_TONE))
-                       {
-                               if (n + 1 == i)
-                               {
-                                       for (i = length; i > n + 1; i--)
-                                               msg[i] = msg[i - 1];
-                                       length++;
-                                       i++;
-                               }
-                               msg[++n] = DTMF_SIGNAL_UNIDENTIFIED_TONE;
-                       }
-                       plci->tone_last_indication_code = dtmf_digit_map[j].character;
-
-                       msg[++n] = dtmf_digit_map[j].character;
-               }
-       }
-       if (n != 0)
-       {
-               msg[0] = (byte) n;
-               sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "wS", SELECTOR_DTMF, msg);
-       }
-}
-
-
-/*------------------------------------------------------------------*/
-/* DTMF parameters                                                  */
-/*------------------------------------------------------------------*/
-
-static void dtmf_parameter_write(PLCI *plci)
-{
-       word i;
-       byte parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE + 2];
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_write",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       parameter_buffer[0] = plci->dtmf_parameter_length + 1;
-       parameter_buffer[1] = DSP_CTRL_SET_DTMF_PARAMETERS;
-       for (i = 0; i < plci->dtmf_parameter_length; i++)
-               parameter_buffer[2 + i] = plci->dtmf_parameter_buffer[i];
-       add_p(plci, FTY, parameter_buffer);
-       sig_req(plci, TEL_CTRL, 0);
-       send_req(plci);
-}
-
-
-static void dtmf_parameter_clear_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->dtmf_parameter_length = 0;
-}
-
-
-static void dtmf_parameter_prepare_switch(dword Id, PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_prepare_switch",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-}
-
-
-static word dtmf_parameter_save_config(dword Id, PLCI *plci, byte Rc)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_save_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       return (GOOD);
-}
-
-
-static word dtmf_parameter_restore_config(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-
-       dbug(1, dprintf("[%06lx] %s,%d: dtmf_parameter_restore_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       if ((plci->B1_facilities & B1_FACILITY_DTMFR)
-           && (plci->dtmf_parameter_length != 0))
-       {
-               switch (plci->adjust_b_state)
-               {
-               case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci->sig_req)
-                       {
-                               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
-                               break;
-                       }
-                       dtmf_parameter_write(plci);
-                       plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_2;
-                       break;
-               case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Restore DTMF parameters failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-                       break;
-               }
-       }
-       return (Info);
-}
-
-
-/*------------------------------------------------------------------*/
-/* Line interconnect facilities                                     */
-/*------------------------------------------------------------------*/
-
-
-LI_CONFIG   *li_config_table;
-word li_total_channels;
-
-
-/*------------------------------------------------------------------*/
-/* translate a CHI information element to a channel number          */
-/* returns 0xff - any channel                                       */
-/*         0xfe - chi wrong coding                                  */
-/*         0xfd - D-channel                                         */
-/*         0x00 - no channel                                        */
-/*         else channel number / PRI: timeslot                      */
-/* if channels is provided we accept more than one channel.         */
-/*------------------------------------------------------------------*/
-
-static byte chi_to_channel(byte *chi, dword *pchannelmap)
-{
-       int p;
-       int i;
-       dword map;
-       byte excl;
-       byte ofs;
-       byte ch;
-
-       if (pchannelmap) *pchannelmap = 0;
-       if (!chi[0]) return 0xff;
-       excl = 0;
-
-       if (chi[1] & 0x20) {
-               if (chi[0] == 1 && chi[1] == 0xac) return 0xfd; /* exclusive d-channel */
-               for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++);
-               if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
-               if ((chi[1] | 0xc8) != 0xe9) return 0xfe;
-               if (chi[1] & 0x08) excl = 0x40;
-
-               /* int. id present */
-               if (chi[1] & 0x40) {
-                       p = i + 1;
-                       for (i = p; i < chi[0] && !(chi[i] & 0x80); i++);
-                       if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
-               }
-
-               /* coding standard, Number/Map, Channel Type */
-               p = i + 1;
-               for (i = p; i < chi[0] && !(chi[i] & 0x80); i++);
-               if (i == chi[0] || !(chi[i] & 0x80)) return 0xfe;
-               if ((chi[p] | 0xd0) != 0xd3) return 0xfe;
-
-               /* Number/Map */
-               if (chi[p] & 0x10) {
-
-                       /* map */
-                       if ((chi[0] - p) == 4) ofs = 0;
-                       else if ((chi[0] - p) == 3) ofs = 1;
-                       else return 0xfe;
-                       ch = 0;
-                       map = 0;
-                       for (i = 0; i < 4 && p < chi[0]; i++) {
-                               p++;
-                               ch += 8;
-                               map <<= 8;
-                               if (chi[p]) {
-                                       for (ch = 0; !(chi[p] & (1 << ch)); ch++);
-                                       map |= chi[p];
-                               }
-                       }
-                       ch += ofs;
-                       map <<= ofs;
-               }
-               else {
-
-                       /* number */
-                       p = i + 1;
-                       ch = chi[p] & 0x3f;
-                       if (pchannelmap) {
-                               if ((byte)(chi[0] - p) > 30) return 0xfe;
-                               map = 0;
-                               for (i = p; i <= chi[0]; i++) {
-                                       if ((chi[i] & 0x7f) > 31) return 0xfe;
-                                       map |= (1L << (chi[i] & 0x7f));
-                               }
-                       }
-                       else {
-                               if (p != chi[0]) return 0xfe;
-                               if (ch > 31) return 0xfe;
-                               map = (1L << ch);
-                       }
-                       if (chi[p] & 0x40) return 0xfe;
-               }
-               if (pchannelmap) *pchannelmap = map;
-               else if (map != ((dword)(1L << ch))) return 0xfe;
-               return (byte)(excl | ch);
-       }
-       else {  /* not PRI */
-               for (i = 1; i < chi[0] && !(chi[i] & 0x80); i++);
-               if (i != chi[0] || !(chi[i] & 0x80)) return 0xfe;
-               if (chi[1] & 0x08) excl = 0x40;
-
-               switch (chi[1] | 0x98) {
-               case 0x98: return 0;
-               case 0x99:
-                       if (pchannelmap) *pchannelmap = 2;
-                       return excl | 1;
-               case 0x9a:
-                       if (pchannelmap) *pchannelmap = 4;
-                       return excl | 2;
-               case 0x9b: return 0xff;
-               case 0x9c: return 0xfd; /* d-ch */
-               default: return 0xfe;
-               }
-       }
-}
-
-
-static void mixer_set_bchannel_id_esc(PLCI *plci, byte bchannel_id)
-{
-       DIVA_CAPI_ADAPTER *a;
-       PLCI *splci;
-       byte old_id;
-
-       a = plci->adapter;
-       old_id = plci->li_bchannel_id;
-       if (a->li_pri)
-       {
-               if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
-                       li_config_table[a->li_base + (old_id - 1)].plci = NULL;
-               plci->li_bchannel_id = (bchannel_id & 0x1f) + 1;
-               if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
-                       li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
-       }
-       else
-       {
-               if (((bchannel_id & 0x03) == 1) || ((bchannel_id & 0x03) == 2))
-               {
-                       if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
-                               li_config_table[a->li_base + (old_id - 1)].plci = NULL;
-                       plci->li_bchannel_id = bchannel_id & 0x03;
-                       if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
-                       {
-                               splci = a->AdvSignalPLCI;
-                               if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
-                               {
-                                       if ((splci->li_bchannel_id != 0)
-                                           && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
-                                       {
-                                               li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
-                                       }
-                                       splci->li_bchannel_id = 3 - plci->li_bchannel_id;
-                                       li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
-                                       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id_esc %d",
-                                                       (dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)),
-                                                       (char *)(FILE_), __LINE__, splci->li_bchannel_id));
-                               }
-                       }
-                       if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
-                               li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
-               }
-       }
-       if ((old_id == 0) && (plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               mixer_clear_config(plci);
-       }
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id_esc %d %d",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, bchannel_id, plci->li_bchannel_id));
-}
-
-
-static void mixer_set_bchannel_id(PLCI *plci, byte *chi)
-{
-       DIVA_CAPI_ADAPTER *a;
-       PLCI *splci;
-       byte ch, old_id;
-
-       a = plci->adapter;
-       old_id = plci->li_bchannel_id;
-       ch = chi_to_channel(chi, NULL);
-       if (!(ch & 0x80))
-       {
-               if (a->li_pri)
-               {
-                       if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
-                               li_config_table[a->li_base + (old_id - 1)].plci = NULL;
-                       plci->li_bchannel_id = (ch & 0x1f) + 1;
-                       if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
-                               li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
-               }
-               else
-               {
-                       if (((ch & 0x1f) == 1) || ((ch & 0x1f) == 2))
-                       {
-                               if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
-                                       li_config_table[a->li_base + (old_id - 1)].plci = NULL;
-                               plci->li_bchannel_id = ch & 0x1f;
-                               if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
-                               {
-                                       splci = a->AdvSignalPLCI;
-                                       if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
-                                       {
-                                               if ((splci->li_bchannel_id != 0)
-                                                   && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
-                                               {
-                                                       li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
-                                               }
-                                               splci->li_bchannel_id = 3 - plci->li_bchannel_id;
-                                               li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
-                                               dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
-                                                               (dword)((splci->Id << 8) | UnMapController(splci->adapter->Id)),
-                                                               (char *)(FILE_), __LINE__, splci->li_bchannel_id));
-                                       }
-                               }
-                               if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
-                                       li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
-                       }
-               }
-       }
-       if ((old_id == 0) && (plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               mixer_clear_config(plci);
-       }
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_set_bchannel_id %02x %d",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, ch, plci->li_bchannel_id));
-}
-
-
-#define MIXER_MAX_DUMP_CHANNELS 34
-
-static void mixer_calculate_coefs(DIVA_CAPI_ADAPTER *a)
-{
-       word n, i, j;
-       char *p;
-       char hex_line[2 * MIXER_MAX_DUMP_CHANNELS + MIXER_MAX_DUMP_CHANNELS / 8 + 4];
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_calculate_coefs",
-                       (dword)(UnMapController(a->Id)), (char *)(FILE_), __LINE__));
-
-       for (i = 0; i < li_total_channels; i++)
-       {
-               li_config_table[i].channel &= LI_CHANNEL_ADDRESSES_SET;
-               if (li_config_table[i].chflags != 0)
-                       li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
-               else
-               {
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               if (((li_config_table[i].flag_table[j]) != 0)
-                                   || ((li_config_table[j].flag_table[i]) != 0))
-                               {
-                                       li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
-                               }
-                               if (((li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) != 0)
-                                   || ((li_config_table[j].flag_table[i] & LI_FLAG_CONFERENCE) != 0))
-                               {
-                                       li_config_table[i].channel |= LI_CHANNEL_CONFERENCE;
-                               }
-                       }
-               }
-       }
-       for (i = 0; i < li_total_channels; i++)
-       {
-               for (j = 0; j < li_total_channels; j++)
-               {
-                       li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC);
-                       if (li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE)
-                               li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
-               }
-       }
-       for (n = 0; n < li_total_channels; n++)
-       {
-               if (li_config_table[n].channel & LI_CHANNEL_CONFERENCE)
-               {
-                       for (i = 0; i < li_total_channels; i++)
-                       {
-                               if (li_config_table[i].channel & LI_CHANNEL_CONFERENCE)
-                               {
-                                       for (j = 0; j < li_total_channels; j++)
-                                       {
-                                               li_config_table[i].coef_table[j] |=
-                                                       li_config_table[i].coef_table[n] & li_config_table[n].coef_table[j];
-                                       }
-                               }
-                       }
-               }
-       }
-       for (i = 0; i < li_total_channels; i++)
-       {
-               if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
-               {
-                       li_config_table[i].coef_table[i] &= ~LI_COEF_CH_CH;
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               if (li_config_table[i].coef_table[j] & LI_COEF_CH_CH)
-                                       li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE;
-                       }
-                       if (li_config_table[i].flag_table[i] & LI_FLAG_CONFERENCE)
-                               li_config_table[i].coef_table[i] |= LI_COEF_CH_CH;
-               }
-       }
-       for (i = 0; i < li_total_channels; i++)
-       {
-               if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
-               {
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
-                                       li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
-                               if (li_config_table[i].flag_table[j] & LI_FLAG_MONITOR)
-                                       li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
-                               if (li_config_table[i].flag_table[j] & LI_FLAG_MIX)
-                                       li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
-                               if (li_config_table[i].flag_table[j] & LI_FLAG_PCCONNECT)
-                                       li_config_table[i].coef_table[j] |= LI_COEF_PC_PC;
-                       }
-                       if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
-                       {
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
-                                       {
-                                               li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
-                                               if (li_config_table[j].chflags & LI_CHFLAG_MIX)
-                                                       li_config_table[i].coef_table[j] |= LI_COEF_PC_CH | LI_COEF_PC_PC;
-                                       }
-                               }
-                       }
-                       if (li_config_table[i].chflags & LI_CHFLAG_MIX)
-                       {
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       if (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT)
-                                               li_config_table[j].coef_table[i] |= LI_COEF_PC_CH;
-                               }
-                       }
-                       if (li_config_table[i].chflags & LI_CHFLAG_LOOP)
-                       {
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
-                                       {
-                                               for (n = 0; n < li_total_channels; n++)
-                                               {
-                                                       if (li_config_table[n].flag_table[i] & LI_FLAG_INTERCONNECT)
-                                                       {
-                                                               li_config_table[n].coef_table[j] |= LI_COEF_CH_CH;
-                                                               if (li_config_table[j].chflags & LI_CHFLAG_MIX)
-                                                               {
-                                                                       li_config_table[n].coef_table[j] |= LI_COEF_PC_CH;
-                                                                       if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
-                                                                               li_config_table[n].coef_table[j] |= LI_COEF_CH_PC | LI_COEF_PC_PC;
-                                                               }
-                                                               else if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
-                                                                       li_config_table[n].coef_table[j] |= LI_COEF_CH_PC;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-       for (i = 0; i < li_total_channels; i++)
-       {
-               if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
-               {
-                       if (li_config_table[i].chflags & (LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP))
-                               li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
-                       if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
-                               li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
-                       if (li_config_table[i].chflags & LI_CHFLAG_MIX)
-                               li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               if ((li_config_table[i].flag_table[j] &
-                                    (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_MONITOR))
-                                   || (li_config_table[j].flag_table[i] &
-                                       (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX)))
-                               {
-                                       li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
-                               }
-                               if (li_config_table[i].flag_table[j] & (LI_FLAG_PCCONNECT | LI_FLAG_MONITOR))
-                                       li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
-                               if (li_config_table[j].flag_table[i] & (LI_FLAG_PCCONNECT | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX))
-                                       li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
-                       }
-                       if (!(li_config_table[i].channel & LI_CHANNEL_ACTIVE))
-                       {
-                               li_config_table[i].coef_table[i] |= LI_COEF_PC_CH | LI_COEF_CH_PC;
-                               li_config_table[i].channel |= LI_CHANNEL_TX_DATA | LI_CHANNEL_RX_DATA;
-                       }
-               }
-       }
-       for (i = 0; i < li_total_channels; i++)
-       {
-               if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
-               {
-                       j = 0;
-                       while ((j < li_total_channels) && !(li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT))
-                               j++;
-                       if (j < li_total_channels)
-                       {
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_PC_CH);
-                                       if (li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT)
-                                               li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
-                               }
-                       }
-               }
-       }
-       n = li_total_channels;
-       if (n > MIXER_MAX_DUMP_CHANNELS)
-               n = MIXER_MAX_DUMP_CHANNELS;
-
-       p = hex_line;
-       for (j = 0; j < n; j++)
-       {
-               if ((j & 0x7) == 0)
-                       *(p++) = ' ';
-               p = hex_byte_pack(p, li_config_table[j].curchnl);
-       }
-       *p = '\0';
-       dbug(1, dprintf("[%06lx] CURRENT %s",
-                       (dword)(UnMapController(a->Id)), (char *)hex_line));
-       p = hex_line;
-       for (j = 0; j < n; j++)
-       {
-               if ((j & 0x7) == 0)
-                       *(p++) = ' ';
-               p = hex_byte_pack(p, li_config_table[j].channel);
-       }
-       *p = '\0';
-       dbug(1, dprintf("[%06lx] CHANNEL %s",
-                       (dword)(UnMapController(a->Id)), (char *)hex_line));
-       p = hex_line;
-       for (j = 0; j < n; j++)
-       {
-               if ((j & 0x7) == 0)
-                       *(p++) = ' ';
-               p = hex_byte_pack(p, li_config_table[j].chflags);
-       }
-       *p = '\0';
-       dbug(1, dprintf("[%06lx] CHFLAG  %s",
-                       (dword)(UnMapController(a->Id)), (char *)hex_line));
-       for (i = 0; i < n; i++)
-       {
-               p = hex_line;
-               for (j = 0; j < n; j++)
-               {
-                       if ((j & 0x7) == 0)
-                               *(p++) = ' ';
-                       p = hex_byte_pack(p, li_config_table[i].flag_table[j]);
-               }
-               *p = '\0';
-               dbug(1, dprintf("[%06lx] FLAG[%02x]%s",
-                               (dword)(UnMapController(a->Id)), i, (char *)hex_line));
-       }
-       for (i = 0; i < n; i++)
-       {
-               p = hex_line;
-               for (j = 0; j < n; j++)
-               {
-                       if ((j & 0x7) == 0)
-                               *(p++) = ' ';
-                       p = hex_byte_pack(p, li_config_table[i].coef_table[j]);
-               }
-               *p = '\0';
-               dbug(1, dprintf("[%06lx] COEF[%02x]%s",
-                               (dword)(UnMapController(a->Id)), i, (char *)hex_line));
-       }
-}
-
-
-static struct
-{
-       byte mask;
-       byte line_flags;
-} mixer_write_prog_pri[] =
-{
-       { LI_COEF_CH_CH, 0 },
-       { LI_COEF_CH_PC, MIXER_COEF_LINE_TO_PC_FLAG },
-       { LI_COEF_PC_CH, MIXER_COEF_LINE_FROM_PC_FLAG },
-       { LI_COEF_PC_PC, MIXER_COEF_LINE_TO_PC_FLAG | MIXER_COEF_LINE_FROM_PC_FLAG }
-};
-
-static struct
-{
-       byte from_ch;
-       byte to_ch;
-       byte mask;
-       byte xconnect_override;
-} mixer_write_prog_bri[] =
-{
-       { 0, 0, LI_COEF_CH_CH, 0x01 },  /* B      to B      */
-       { 1, 0, LI_COEF_CH_CH, 0x01 },  /* Alt B  to B      */
-       { 0, 0, LI_COEF_PC_CH, 0x80 },  /* PC     to B      */
-       { 1, 0, LI_COEF_PC_CH, 0x01 },  /* Alt PC to B      */
-       { 2, 0, LI_COEF_CH_CH, 0x00 },  /* IC     to B      */
-       { 3, 0, LI_COEF_CH_CH, 0x00 },  /* Alt IC to B      */
-       { 0, 0, LI_COEF_CH_PC, 0x80 },  /* B      to PC     */
-       { 1, 0, LI_COEF_CH_PC, 0x01 },  /* Alt B  to PC     */
-       { 0, 0, LI_COEF_PC_PC, 0x01 },  /* PC     to PC     */
-       { 1, 0, LI_COEF_PC_PC, 0x01 },  /* Alt PC to PC     */
-       { 2, 0, LI_COEF_CH_PC, 0x00 },  /* IC     to PC     */
-       { 3, 0, LI_COEF_CH_PC, 0x00 },  /* Alt IC to PC     */
-       { 0, 2, LI_COEF_CH_CH, 0x00 },  /* B      to IC     */
-       { 1, 2, LI_COEF_CH_CH, 0x00 },  /* Alt B  to IC     */
-       { 0, 2, LI_COEF_PC_CH, 0x00 },  /* PC     to IC     */
-       { 1, 2, LI_COEF_PC_CH, 0x00 },  /* Alt PC to IC     */
-       { 2, 2, LI_COEF_CH_CH, 0x00 },  /* IC     to IC     */
-       { 3, 2, LI_COEF_CH_CH, 0x00 },  /* Alt IC to IC     */
-       { 1, 1, LI_COEF_CH_CH, 0x01 },  /* Alt B  to Alt B  */
-       { 0, 1, LI_COEF_CH_CH, 0x01 },  /* B      to Alt B  */
-       { 1, 1, LI_COEF_PC_CH, 0x80 },  /* Alt PC to Alt B  */
-       { 0, 1, LI_COEF_PC_CH, 0x01 },  /* PC     to Alt B  */
-       { 3, 1, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt B  */
-       { 2, 1, LI_COEF_CH_CH, 0x00 },  /* IC     to Alt B  */
-       { 1, 1, LI_COEF_CH_PC, 0x80 },  /* Alt B  to Alt PC */
-       { 0, 1, LI_COEF_CH_PC, 0x01 },  /* B      to Alt PC */
-       { 1, 1, LI_COEF_PC_PC, 0x01 },  /* Alt PC to Alt PC */
-       { 0, 1, LI_COEF_PC_PC, 0x01 },  /* PC     to Alt PC */
-       { 3, 1, LI_COEF_CH_PC, 0x00 },  /* Alt IC to Alt PC */
-       { 2, 1, LI_COEF_CH_PC, 0x00 },  /* IC     to Alt PC */
-       { 1, 3, LI_COEF_CH_CH, 0x00 },  /* Alt B  to Alt IC */
-       { 0, 3, LI_COEF_CH_CH, 0x00 },  /* B      to Alt IC */
-       { 1, 3, LI_COEF_PC_CH, 0x00 },  /* Alt PC to Alt IC */
-       { 0, 3, LI_COEF_PC_CH, 0x00 },  /* PC     to Alt IC */
-       { 3, 3, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt IC */
-       { 2, 3, LI_COEF_CH_CH, 0x00 }   /* IC     to Alt IC */
-};
-
-static byte mixer_swapped_index_bri[] =
-{
-       18,  /* B      to B      */
-       19,  /* Alt B  to B      */
-       20,  /* PC     to B      */
-       21,  /* Alt PC to B      */
-       22,  /* IC     to B      */
-       23,  /* Alt IC to B      */
-       24,  /* B      to PC     */
-       25,  /* Alt B  to PC     */
-       26,  /* PC     to PC     */
-       27,  /* Alt PC to PC     */
-       28,  /* IC     to PC     */
-       29,  /* Alt IC to PC     */
-       30,  /* B      to IC     */
-       31,  /* Alt B  to IC     */
-       32,  /* PC     to IC     */
-       33,  /* Alt PC to IC     */
-       34,  /* IC     to IC     */
-       35,  /* Alt IC to IC     */
-       0,   /* Alt B  to Alt B  */
-       1,   /* B      to Alt B  */
-       2,   /* Alt PC to Alt B  */
-       3,   /* PC     to Alt B  */
-       4,   /* Alt IC to Alt B  */
-       5,   /* IC     to Alt B  */
-       6,   /* Alt B  to Alt PC */
-       7,   /* B      to Alt PC */
-       8,   /* Alt PC to Alt PC */
-       9,   /* PC     to Alt PC */
-       10,  /* Alt IC to Alt PC */
-       11,  /* IC     to Alt PC */
-       12,  /* Alt B  to Alt IC */
-       13,  /* B      to Alt IC */
-       14,  /* Alt PC to Alt IC */
-       15,  /* PC     to Alt IC */
-       16,  /* Alt IC to Alt IC */
-       17   /* IC     to Alt IC */
-};
-
-static struct
-{
-       byte mask;
-       byte from_pc;
-       byte to_pc;
-} xconnect_write_prog[] =
-{
-       { LI_COEF_CH_CH, false, false },
-       { LI_COEF_CH_PC, false, true },
-       { LI_COEF_PC_CH, true, false },
-       { LI_COEF_PC_PC, true, true }
-};
-
-
-static void xconnect_query_addresses(PLCI *plci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word w, ch;
-       byte *p;
-
-       dbug(1, dprintf("[%06lx] %s,%d: xconnect_query_addresses",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       a = plci->adapter;
-       if (a->li_pri && ((plci->li_bchannel_id == 0)
-                         || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)))
-       {
-               dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out",
-                               (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                               (char *)(FILE_), __LINE__));
-               return;
-       }
-       p = plci->internal_req_buffer;
-       ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
-       *(p++) = UDATA_REQUEST_XCONNECT_FROM;
-       w = ch;
-       *(p++) = (byte) w;
-       *(p++) = (byte)(w >> 8);
-       w = ch | XCONNECT_CHANNEL_PORT_PC;
-       *(p++) = (byte) w;
-       *(p++) = (byte)(w >> 8);
-       plci->NData[0].P = plci->internal_req_buffer;
-       plci->NData[0].PLength = p - plci->internal_req_buffer;
-       plci->NL.X = plci->NData;
-       plci->NL.ReqCh = 0;
-       plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-       plci->adapter->request(&plci->NL);
-}
-
-
-static void xconnect_write_coefs(PLCI *plci, word internal_command)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: xconnect_write_coefs %04x",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, internal_command));
-
-       plci->li_write_command = internal_command;
-       plci->li_write_channel = 0;
-}
-
-
-static byte xconnect_write_coefs_process(dword Id, PLCI *plci, byte Rc)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word w, n, i, j, r, s, to_ch;
-       dword d;
-       byte *p;
-       struct xconnect_transfer_address_s   *transfer_address;
-       byte ch_map[MIXER_CHANNELS_BRI];
-
-       dbug(1, dprintf("[%06x] %s,%d: xconnect_write_coefs_process %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->li_write_channel));
-
-       a = plci->adapter;
-       if ((plci->li_bchannel_id == 0)
-           || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
-       {
-               dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               return (true);
-       }
-       i = a->li_base + (plci->li_bchannel_id - 1);
-       j = plci->li_write_channel;
-       p = plci->internal_req_buffer;
-       if (j != 0)
-       {
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: LI write coefs failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       return (false);
-               }
-       }
-       if (li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-       {
-               r = 0;
-               s = 0;
-               if (j < li_total_channels)
-               {
-                       if (li_config_table[i].channel & LI_CHANNEL_ADDRESSES_SET)
-                       {
-                               s = ((li_config_table[i].send_b.card_address.low | li_config_table[i].send_b.card_address.high) ?
-                                    (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_PC | LI_COEF_PC_PC)) &
-                                       ((li_config_table[i].send_pc.card_address.low | li_config_table[i].send_pc.card_address.high) ?
-                                        (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_PC_CH));
-                       }
-                       r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                       while ((j < li_total_channels)
-                              && ((r == 0)
-                                  || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
-                                  || (!li_config_table[j].adapter->li_pri
-                                      && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
-                                  || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
-                                       || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
-                                      && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
-                                          || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
-                                  || ((li_config_table[j].adapter->li_base != a->li_base)
-                                      && !(r & s &
-                                           ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
-                                            (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
-                                           ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
-                                            (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))))
-                       {
-                               j++;
-                               if (j < li_total_channels)
-                                       r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                       }
-               }
-               if (j < li_total_channels)
-               {
-                       plci->internal_command = plci->li_write_command;
-                       if (plci_nl_busy(plci))
-                               return (true);
-                       to_ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
-                       *(p++) = UDATA_REQUEST_XCONNECT_TO;
-                       do
-                       {
-                               if (li_config_table[j].adapter->li_base != a->li_base)
-                               {
-                                       r &= s &
-                                               ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
-                                                (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
-                                               ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
-                                                (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC));
-                               }
-                               n = 0;
-                               do
-                               {
-                                       if (r & xconnect_write_prog[n].mask)
-                                       {
-                                               if (xconnect_write_prog[n].from_pc)
-                                                       transfer_address = &(li_config_table[j].send_pc);
-                                               else
-                                                       transfer_address = &(li_config_table[j].send_b);
-                                               d = transfer_address->card_address.low;
-                                               *(p++) = (byte) d;
-                                               *(p++) = (byte)(d >> 8);
-                                               *(p++) = (byte)(d >> 16);
-                                               *(p++) = (byte)(d >> 24);
-                                               d = transfer_address->card_address.high;
-                                               *(p++) = (byte) d;
-                                               *(p++) = (byte)(d >> 8);
-                                               *(p++) = (byte)(d >> 16);
-                                               *(p++) = (byte)(d >> 24);
-                                               d = transfer_address->offset;
-                                               *(p++) = (byte) d;
-                                               *(p++) = (byte)(d >> 8);
-                                               *(p++) = (byte)(d >> 16);
-                                               *(p++) = (byte)(d >> 24);
-                                               w = xconnect_write_prog[n].to_pc ? to_ch | XCONNECT_CHANNEL_PORT_PC : to_ch;
-                                               *(p++) = (byte) w;
-                                               *(p++) = (byte)(w >> 8);
-                                               w = ((li_config_table[i].coef_table[j] & xconnect_write_prog[n].mask) == 0) ? 0x01 :
-                                                       (li_config_table[i].adapter->u_law ?
-                                                        (li_config_table[j].adapter->u_law ? 0x80 : 0x86) :
-                                                        (li_config_table[j].adapter->u_law ? 0x7a : 0x80));
-                                               *(p++) = (byte) w;
-                                               *(p++) = (byte) 0;
-                                               li_config_table[i].coef_table[j] ^= xconnect_write_prog[n].mask << 4;
-                                       }
-                                       n++;
-                               } while ((n < ARRAY_SIZE(xconnect_write_prog))
-                                        && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
-                               if (n == ARRAY_SIZE(xconnect_write_prog))
-                               {
-                                       do
-                                       {
-                                               j++;
-                                               if (j < li_total_channels)
-                                                       r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                                       } while ((j < li_total_channels)
-                                                && ((r == 0)
-                                                    || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
-                                                    || (!li_config_table[j].adapter->li_pri
-                                                        && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
-                                                    || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
-                                                         || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
-                                                        && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
-                                                            || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
-                                                    || ((li_config_table[j].adapter->li_base != a->li_base)
-                                                        && !(r & s &
-                                                             ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
-                                                              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
-                                                             ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
-                                                              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))));
-                               }
-                       } while ((j < li_total_channels)
-                                && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
-               }
-               else if (j == li_total_channels)
-               {
-                       plci->internal_command = plci->li_write_command;
-                       if (plci_nl_busy(plci))
-                               return (true);
-                       if (a->li_pri)
-                       {
-                               *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
-                               w = 0;
-                               if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_TX_DATA;
-                               if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_RX_DATA;
-                               *(p++) = (byte) w;
-                               *(p++) = (byte)(w >> 8);
-                       }
-                       else
-                       {
-                               *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
-                               w = 0;
-                               if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
-                                   && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
-                               {
-                                       w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
-                               }
-                               if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_TX_DATA;
-                               if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_RX_DATA;
-                               *(p++) = (byte) w;
-                               *(p++) = (byte)(w >> 8);
-                               for (j = 0; j < sizeof(ch_map); j += 2)
-                               {
-                                       if (plci->li_bchannel_id == 2)
-                                       {
-                                               ch_map[j] = (byte)(j + 1);
-                                               ch_map[j + 1] = (byte) j;
-                                       }
-                                       else
-                                       {
-                                               ch_map[j] = (byte) j;
-                                               ch_map[j + 1] = (byte)(j + 1);
-                                       }
-                               }
-                               for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
-                               {
-                                       i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
-                                       j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
-                                       if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
-                                       {
-                                               *p = (mixer_write_prog_bri[n].xconnect_override != 0) ?
-                                                       mixer_write_prog_bri[n].xconnect_override :
-                                                       ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
-                                               if ((i >= a->li_base + MIXER_BCHANNELS_BRI) || (j >= a->li_base + MIXER_BCHANNELS_BRI))
-                                               {
-                                                       w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                                                       li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
-                                               }
-                                       }
-                                       else
-                                       {
-                                               *p = 0x00;
-                                               if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
-                                               {
-                                                       w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
-                                                       if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
-                                                               *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
-                                               }
-                                       }
-                                       p++;
-                               }
-                       }
-                       j = li_total_channels + 1;
-               }
-       }
-       else
-       {
-               if (j <= li_total_channels)
-               {
-                       plci->internal_command = plci->li_write_command;
-                       if (plci_nl_busy(plci))
-                               return (true);
-                       if (j < a->li_base)
-                               j = a->li_base;
-                       if (a->li_pri)
-                       {
-                               *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
-                               w = 0;
-                               if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_TX_DATA;
-                               if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_RX_DATA;
-                               *(p++) = (byte) w;
-                               *(p++) = (byte)(w >> 8);
-                               for (n = 0; n < ARRAY_SIZE(mixer_write_prog_pri); n++)
-                               {
-                                       *(p++) = (byte)((plci->li_bchannel_id - 1) | mixer_write_prog_pri[n].line_flags);
-                                       for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
-                                       {
-                                               w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                                               if (w & mixer_write_prog_pri[n].mask)
-                                               {
-                                                       *(p++) = (li_config_table[i].coef_table[j] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
-                                                       li_config_table[i].coef_table[j] ^= mixer_write_prog_pri[n].mask << 4;
-                                               }
-                                               else
-                                                       *(p++) = 0x00;
-                                       }
-                                       *(p++) = (byte)((plci->li_bchannel_id - 1) | MIXER_COEF_LINE_ROW_FLAG | mixer_write_prog_pri[n].line_flags);
-                                       for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
-                                       {
-                                               w = ((li_config_table[j].coef_table[i] & 0xf) ^ (li_config_table[j].coef_table[i] >> 4));
-                                               if (w & mixer_write_prog_pri[n].mask)
-                                               {
-                                                       *(p++) = (li_config_table[j].coef_table[i] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
-                                                       li_config_table[j].coef_table[i] ^= mixer_write_prog_pri[n].mask << 4;
-                                               }
-                                               else
-                                                       *(p++) = 0x00;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
-                               w = 0;
-                               if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
-                                   && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
-                               {
-                                       w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
-                               }
-                               if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_TX_DATA;
-                               if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
-                                       w |= MIXER_FEATURE_ENABLE_RX_DATA;
-                               *(p++) = (byte) w;
-                               *(p++) = (byte)(w >> 8);
-                               for (j = 0; j < sizeof(ch_map); j += 2)
-                               {
-                                       if (plci->li_bchannel_id == 2)
-                                       {
-                                               ch_map[j] = (byte)(j + 1);
-                                               ch_map[j + 1] = (byte) j;
-                                       }
-                                       else
-                                       {
-                                               ch_map[j] = (byte) j;
-                                               ch_map[j + 1] = (byte)(j + 1);
-                                       }
-                               }
-                               for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
-                               {
-                                       i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
-                                       j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
-                                       if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
-                                       {
-                                               *p = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
-                                               w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                                               li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
-                                       }
-                                       else
-                                       {
-                                               *p = 0x00;
-                                               if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
-                                               {
-                                                       w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
-                                                       if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
-                                                               *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
-                                               }
-                                       }
-                                       p++;
-                               }
-                       }
-                       j = li_total_channels + 1;
-               }
-       }
-       plci->li_write_channel = j;
-       if (p != plci->internal_req_buffer)
-       {
-               plci->NData[0].P = plci->internal_req_buffer;
-               plci->NData[0].PLength = p - plci->internal_req_buffer;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-               plci->adapter->request(&plci->NL);
-       }
-       return (true);
-}
-
-
-static void mixer_notify_update(PLCI *plci, byte others)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word i, w;
-       PLCI *notify_plci;
-       byte msg[sizeof(CAPI_MSG_HEADER) + 6];
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_notify_update %d",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, others));
-
-       a = plci->adapter;
-       if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
-       {
-               if (others)
-                       plci->li_notify_update = true;
-               i = 0;
-               do
-               {
-                       notify_plci = NULL;
-                       if (others)
-                       {
-                               while ((i < li_total_channels) && (li_config_table[i].plci == NULL))
-                                       i++;
-                               if (i < li_total_channels)
-                                       notify_plci = li_config_table[i++].plci;
-                       }
-                       else
-                       {
-                               if ((plci->li_bchannel_id != 0)
-                                   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-                               {
-                                       notify_plci = plci;
-                               }
-                       }
-                       if ((notify_plci != NULL)
-                           && !notify_plci->li_notify_update
-                           && (notify_plci->appl != NULL)
-                           && (notify_plci->State)
-                           && notify_plci->NL.Id && !notify_plci->nl_remove_id)
-                       {
-                               notify_plci->li_notify_update = true;
-                               ((CAPI_MSG *) msg)->header.length = 18;
-                               ((CAPI_MSG *) msg)->header.appl_id = notify_plci->appl->Id;
-                               ((CAPI_MSG *) msg)->header.command = _FACILITY_R;
-                               ((CAPI_MSG *) msg)->header.number = 0;
-                               ((CAPI_MSG *) msg)->header.controller = notify_plci->adapter->Id;
-                               ((CAPI_MSG *) msg)->header.plci = notify_plci->Id;
-                               ((CAPI_MSG *) msg)->header.ncci = 0;
-                               ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
-                               ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
-                               ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff;
-                               ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8;
-                               ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
-                               w = api_put(notify_plci->appl, (CAPI_MSG *) msg);
-                               if (w != _QUEUE_FULL)
-                               {
-                                       if (w != 0)
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: Interconnect notify failed %06x %d",
-                                                               (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                                                               (char *)(FILE_), __LINE__,
-                                                               (dword)((notify_plci->Id << 8) | UnMapController(notify_plci->adapter->Id)), w));
-                                       }
-                                       notify_plci->li_notify_update = false;
-                               }
-                       }
-               } while (others && (notify_plci != NULL));
-               if (others)
-                       plci->li_notify_update = false;
-       }
-}
-
-
-static void mixer_clear_config(PLCI *plci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word i, j;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->li_notify_update = false;
-       plci->li_plci_b_write_pos = 0;
-       plci->li_plci_b_read_pos = 0;
-       plci->li_plci_b_req_pos = 0;
-       a = plci->adapter;
-       if ((plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               i = a->li_base + (plci->li_bchannel_id - 1);
-               li_config_table[i].curchnl = 0;
-               li_config_table[i].channel = 0;
-               li_config_table[i].chflags = 0;
-               for (j = 0; j < li_total_channels; j++)
-               {
-                       li_config_table[j].flag_table[i] = 0;
-                       li_config_table[i].flag_table[j] = 0;
-                       li_config_table[i].coef_table[j] = 0;
-                       li_config_table[j].coef_table[i] = 0;
-               }
-               if (!a->li_pri)
-               {
-                       li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
-                       if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
-                       {
-                               i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
-                               li_config_table[i].curchnl = 0;
-                               li_config_table[i].channel = 0;
-                               li_config_table[i].chflags = 0;
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       li_config_table[i].flag_table[j] = 0;
-                                       li_config_table[j].flag_table[i] = 0;
-                                       li_config_table[i].coef_table[j] = 0;
-                                       li_config_table[j].coef_table[i] = 0;
-                               }
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                               {
-                                       i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
-                                       li_config_table[i].curchnl = 0;
-                                       li_config_table[i].channel = 0;
-                                       li_config_table[i].chflags = 0;
-                                       for (j = 0; j < li_total_channels; j++)
-                                       {
-                                               li_config_table[i].flag_table[j] = 0;
-                                               li_config_table[j].flag_table[i] = 0;
-                                               li_config_table[i].coef_table[j] = 0;
-                                               li_config_table[j].coef_table[i] = 0;
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-
-static void mixer_prepare_switch(dword Id, PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_prepare_switch",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       do
-       {
-               mixer_indication_coefs_set(Id, plci);
-       } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
-}
-
-
-static word mixer_save_config(dword Id, PLCI *plci, byte Rc)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word i, j;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_save_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       a = plci->adapter;
-       if ((plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               i = a->li_base + (plci->li_bchannel_id - 1);
-               for (j = 0; j < li_total_channels; j++)
-               {
-                       li_config_table[i].coef_table[j] &= 0xf;
-                       li_config_table[j].coef_table[i] &= 0xf;
-               }
-               if (!a->li_pri)
-                       li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
-       }
-       return (GOOD);
-}
-
-
-static word mixer_restore_config(dword Id, PLCI *plci, byte Rc)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word Info;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_restore_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       a = plci->adapter;
-       if ((plci->B1_facilities & B1_FACILITY_MIXER)
-           && (plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               switch (plci->adjust_b_state)
-               {
-               case ADJUST_B_RESTORE_MIXER_1:
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               if (plci_nl_busy(plci))
-                               {
-                                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
-                                       break;
-                               }
-                               xconnect_query_addresses(plci);
-                               plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_2;
-                               break;
-                       }
-                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
-                       Rc = OK;
-                       /* fall through */
-               case ADJUST_B_RESTORE_MIXER_2:
-               case ADJUST_B_RESTORE_MIXER_3:
-               case ADJUST_B_RESTORE_MIXER_4:
-                       if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Adjust B query addresses failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-                       if (Rc == OK)
-                       {
-                               if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
-                                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_3;
-                               else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)
-                                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
-                       }
-                       else if (Rc == 0)
-                       {
-                               if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
-                                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_4;
-                               else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
-                                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
-                       }
-                       if (plci->adjust_b_state != ADJUST_B_RESTORE_MIXER_5)
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               break;
-                       }
-                       /* fall through */
-               case ADJUST_B_RESTORE_MIXER_5:
-                       xconnect_write_coefs(plci, plci->adjust_b_command);
-                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_6;
-                       Rc = OK;
-                       /* fall through */
-               case ADJUST_B_RESTORE_MIXER_6:
-                       if (!xconnect_write_coefs_process(Id, plci, Rc))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               break;
-                       plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_7;
-               case ADJUST_B_RESTORE_MIXER_7:
-                       break;
-               }
-       }
-       return (Info);
-}
-
-
-static void mixer_command(dword Id, PLCI *plci, byte Rc)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word i, internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_command %02x %04x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
-                       plci->li_cmd));
-
-       a = plci->adapter;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (plci->li_cmd)
-       {
-       case LI_REQ_CONNECT:
-       case LI_REQ_DISCONNECT:
-       case LI_REQ_SILENT_UPDATE:
-               switch (internal_command)
-               {
-               default:
-                       if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
-                       {
-                               adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
-                                                                         B1_FACILITY_MIXER), MIXER_COMMAND_1);
-                       }
-                       /* fall through */
-               case MIXER_COMMAND_1:
-                       if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
-                       {
-                               if (adjust_b_process(Id, plci, Rc) != GOOD)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Load mixer failed",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       break;
-                               }
-                               if (plci->internal_command)
-                                       return;
-                       }
-                       plci->li_plci_b_req_pos = plci->li_plci_b_write_pos;
-                       if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
-                           || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER)
-                               && (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities &
-                                                                                     ~B1_FACILITY_MIXER)) == plci->B1_resource)))
-                       {
-                               xconnect_write_coefs(plci, MIXER_COMMAND_2);
-                       }
-                       else
-                       {
-                               do
-                               {
-                                       mixer_indication_coefs_set(Id, plci);
-                               } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
-                       }
-                       /* fall through */
-               case MIXER_COMMAND_2:
-                       if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
-                           || ((get_b1_facilities(plci, plci->B1_resource) & B1_FACILITY_MIXER)
-                               && (add_b1_facilities(plci, plci->B1_resource, (word)(plci->B1_facilities &
-                                                                                     ~B1_FACILITY_MIXER)) == plci->B1_resource)))
-                       {
-                               if (!xconnect_write_coefs_process(Id, plci, Rc))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Write mixer coefs failed",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       if (plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
-                                       {
-                                               do
-                                               {
-                                                       plci->li_plci_b_write_pos = (plci->li_plci_b_write_pos == 0) ?
-                                                               LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1;
-                                                       i = (plci->li_plci_b_write_pos == 0) ?
-                                                               LI_PLCI_B_QUEUE_ENTRIES - 1 : plci->li_plci_b_write_pos - 1;
-                                               } while ((plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
-                                                        && !(plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG));
-                                       }
-                                       break;
-                               }
-                               if (plci->internal_command)
-                                       return;
-                       }
-                       if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
-                       {
-                               adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
-                                                                         ~B1_FACILITY_MIXER), MIXER_COMMAND_3);
-                       }
-                       /* fall through */
-               case MIXER_COMMAND_3:
-                       if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
-                       {
-                               if (adjust_b_process(Id, plci, Rc) != GOOD)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Unload mixer failed",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       break;
-                               }
-                               if (plci->internal_command)
-                                       return;
-                       }
-                       break;
-               }
-               break;
-       }
-       if ((plci->li_bchannel_id == 0)
-           || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
-       {
-               dbug(1, dprintf("[%06x] %s,%d: Channel id wiped out %d",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, (int)(plci->li_bchannel_id)));
-       }
-       else
-       {
-               i = a->li_base + (plci->li_bchannel_id - 1);
-               li_config_table[i].curchnl = plci->li_channel_bits;
-               if (!a->li_pri && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
-               {
-                       i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
-                       li_config_table[i].curchnl = plci->li_channel_bits;
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                       {
-                               i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
-                               li_config_table[i].curchnl = plci->li_channel_bits;
-                       }
-               }
-       }
-}
-
-
-static void li_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
-                             dword plci_b_id, byte connect, dword li_flags)
-{
-       word i, ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
-       PLCI *plci_b;
-       DIVA_CAPI_ADAPTER *a_b;
-
-       a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]);
-       plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
-       ch_a = a->li_base + (plci->li_bchannel_id - 1);
-       if (!a->li_pri && (plci->tel == ADV_VOICE)
-           && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
-       {
-               ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
-               ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
-                       a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
-       }
-       else
-       {
-               ch_a_v = ch_a;
-               ch_a_s = ch_a;
-       }
-       ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
-       if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
-           && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
-       {
-               ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
-               ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
-                       a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
-       }
-       else
-       {
-               ch_b_v = ch_b;
-               ch_b_s = ch_b;
-       }
-       if (connect)
-       {
-               li_config_table[ch_a].flag_table[ch_a_v] &= ~LI_FLAG_MONITOR;
-               li_config_table[ch_a].flag_table[ch_a_s] &= ~LI_FLAG_MONITOR;
-               li_config_table[ch_a_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
-               li_config_table[ch_a_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
-       }
-       li_config_table[ch_a].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
-       li_config_table[ch_a].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
-       li_config_table[ch_b_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
-       li_config_table[ch_b_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
-       if (ch_a_v == ch_b_v)
-       {
-               li_config_table[ch_a_v].flag_table[ch_b_v] &= ~LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_s].flag_table[ch_b_s] &= ~LI_FLAG_CONFERENCE;
-       }
-       else
-       {
-               if (li_config_table[ch_a_v].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
-               {
-                       for (i = 0; i < li_total_channels; i++)
-                       {
-                               if (i != ch_a_v)
-                                       li_config_table[ch_a_v].flag_table[i] &= ~LI_FLAG_CONFERENCE;
-                       }
-               }
-               if (li_config_table[ch_a_s].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
-               {
-                       for (i = 0; i < li_total_channels; i++)
-                       {
-                               if (i != ch_a_s)
-                                       li_config_table[ch_a_s].flag_table[i] &= ~LI_FLAG_CONFERENCE;
-                       }
-               }
-               if (li_config_table[ch_b_v].flag_table[ch_a_v] & LI_FLAG_CONFERENCE)
-               {
-                       for (i = 0; i < li_total_channels; i++)
-                       {
-                               if (i != ch_a_v)
-                                       li_config_table[i].flag_table[ch_a_v] &= ~LI_FLAG_CONFERENCE;
-                       }
-               }
-               if (li_config_table[ch_b_v].flag_table[ch_a_s] & LI_FLAG_CONFERENCE)
-               {
-                       for (i = 0; i < li_total_channels; i++)
-                       {
-                               if (i != ch_a_s)
-                                       li_config_table[i].flag_table[ch_a_s] &= ~LI_FLAG_CONFERENCE;
-                       }
-               }
-       }
-       if (li_flags & LI_FLAG_CONFERENCE_A_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
-       }
-       if (li_flags & LI_FLAG_CONFERENCE_B_A)
-       {
-               li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
-       }
-       if (li_flags & LI_FLAG_MONITOR_A)
-       {
-               li_config_table[ch_a].flag_table[ch_a_v] |= LI_FLAG_MONITOR;
-               li_config_table[ch_a].flag_table[ch_a_s] |= LI_FLAG_MONITOR;
-       }
-       if (li_flags & LI_FLAG_MONITOR_B)
-       {
-               li_config_table[ch_a].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
-               li_config_table[ch_a].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
-       }
-       if (li_flags & LI_FLAG_ANNOUNCEMENT_A)
-       {
-               li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
-               li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
-       }
-       if (li_flags & LI_FLAG_ANNOUNCEMENT_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
-               li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
-       }
-       if (li_flags & LI_FLAG_MIX_A)
-       {
-               li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_MIX;
-               li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_MIX;
-       }
-       if (li_flags & LI_FLAG_MIX_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_MIX;
-               li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_MIX;
-       }
-       if (ch_a_v != ch_a_s)
-       {
-               li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
-       }
-       if (ch_b_v != ch_b_s)
-       {
-               li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
-       }
-}
-
-
-static void li2_update_connect(dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
-                              dword plci_b_id, byte connect, dword li_flags)
-{
-       word ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
-       PLCI *plci_b;
-       DIVA_CAPI_ADAPTER *a_b;
-
-       a_b = &(adapter[MapController((byte)(plci_b_id & 0x7f)) - 1]);
-       plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
-       ch_a = a->li_base + (plci->li_bchannel_id - 1);
-       if (!a->li_pri && (plci->tel == ADV_VOICE)
-           && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
-       {
-               ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
-               ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
-                       a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
-       }
-       else
-       {
-               ch_a_v = ch_a;
-               ch_a_s = ch_a;
-       }
-       ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
-       if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
-           && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
-       {
-               ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
-               ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
-                       a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
-       }
-       else
-       {
-               ch_b_v = ch_b;
-               ch_b_s = ch_b;
-       }
-       if (connect)
-       {
-               li_config_table[ch_b].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
-               li_config_table[ch_b].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
-               li_config_table[ch_b_v].flag_table[ch_b] &= ~LI_FLAG_MIX;
-               li_config_table[ch_b_s].flag_table[ch_b] &= ~LI_FLAG_MIX;
-               li_config_table[ch_b].flag_table[ch_b] &= ~LI_FLAG_PCCONNECT;
-               li_config_table[ch_b].chflags &= ~(LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP);
-       }
-       li_config_table[ch_b_v].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_b_s].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_b_v].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_b_s].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_a_v].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_a_v].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_a_s].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       li_config_table[ch_a_s].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
-       if (li_flags & LI2_FLAG_INTERCONNECT_A_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
-       }
-       if (li_flags & LI2_FLAG_INTERCONNECT_B_A)
-       {
-               li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
-       }
-       if (li_flags & LI2_FLAG_MONITOR_B)
-       {
-               li_config_table[ch_b].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
-               li_config_table[ch_b].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
-       }
-       if (li_flags & LI2_FLAG_MIX_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_b] |= LI_FLAG_MIX;
-               li_config_table[ch_b_s].flag_table[ch_b] |= LI_FLAG_MIX;
-       }
-       if (li_flags & LI2_FLAG_MONITOR_X)
-               li_config_table[ch_b].chflags |= LI_CHFLAG_MONITOR;
-       if (li_flags & LI2_FLAG_MIX_X)
-               li_config_table[ch_b].chflags |= LI_CHFLAG_MIX;
-       if (li_flags & LI2_FLAG_LOOP_B)
-       {
-               li_config_table[ch_b_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
-               li_config_table[ch_b_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
-       }
-       if (li_flags & LI2_FLAG_LOOP_PC)
-               li_config_table[ch_b].flag_table[ch_b] |= LI_FLAG_PCCONNECT;
-       if (li_flags & LI2_FLAG_LOOP_X)
-               li_config_table[ch_b].chflags |= LI_CHFLAG_LOOP;
-       if (li_flags & LI2_FLAG_PCCONNECT_A_B)
-               li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_PCCONNECT;
-       if (li_flags & LI2_FLAG_PCCONNECT_B_A)
-               li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_PCCONNECT;
-       if (ch_a_v != ch_a_s)
-       {
-               li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
-       }
-       if (ch_b_v != ch_b_s)
-       {
-               li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
-               li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
-       }
-}
-
-
-static word li_check_main_plci(dword Id, PLCI *plci)
-{
-       if (plci == NULL)
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               return (_WRONG_IDENTIFIER);
-       }
-       if (!plci->State
-           || !plci->NL.Id || plci->nl_remove_id
-           || (plci->li_bchannel_id == 0))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               return (_WRONG_STATE);
-       }
-       li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = plci;
-       return (GOOD);
-}
-
-
-static PLCI *li_check_plci_b(dword Id, PLCI *plci,
-                            dword plci_b_id, word plci_b_write_pos, byte *p_result)
-{
-       byte ctlr_b;
-       PLCI *plci_b;
-
-       if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
-            LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
-               return (NULL);
-       }
-       ctlr_b = 0;
-       if ((plci_b_id & 0x7f) != 0)
-       {
-               ctlr_b = MapController((byte)(plci_b_id & 0x7f));
-               if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
-                       ctlr_b = 0;
-       }
-       if ((ctlr_b == 0)
-           || (((plci_b_id >> 8) & 0xff) == 0)
-           || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _WRONG_IDENTIFIER);
-               return (NULL);
-       }
-       plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
-       if (!plci_b->State
-           || !plci_b->NL.Id || plci_b->nl_remove_id
-           || (plci_b->li_bchannel_id == 0))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
-               return (NULL);
-       }
-       li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci = plci_b;
-       if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
-           ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER))
-           && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-               || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _WRONG_IDENTIFIER);
-               return (NULL);
-       }
-       if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource,
-                                                         (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
-               PUT_WORD(p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
-               return (NULL);
-       }
-       return (plci_b);
-}
-
-
-static PLCI *li2_check_plci_b(dword Id, PLCI *plci,
-                             dword plci_b_id, word plci_b_write_pos, byte *p_result)
-{
-       byte ctlr_b;
-       PLCI *plci_b;
-
-       if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
-            LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               PUT_WORD(p_result, _WRONG_STATE);
-               return (NULL);
-       }
-       ctlr_b = 0;
-       if ((plci_b_id & 0x7f) != 0)
-       {
-               ctlr_b = MapController((byte)(plci_b_id & 0x7f));
-               if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
-                       ctlr_b = 0;
-       }
-       if ((ctlr_b == 0)
-           || (((plci_b_id >> 8) & 0xff) == 0)
-           || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI invalid second PLCI %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _WRONG_IDENTIFIER);
-               return (NULL);
-       }
-       plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
-       if (!plci_b->State
-           || !plci_b->NL.Id || plci_b->nl_remove_id
-           || (plci_b->li_bchannel_id == 0)
-           || (li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci != plci_b))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI peer in wrong state %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _WRONG_STATE);
-               return (NULL);
-       }
-       if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
-           ((byte)(UnMapController(plci->adapter->Id) & ~EXT_CONTROLLER))
-           && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-               || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI not on same ctrl %08lx",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b_id));
-               PUT_WORD(p_result, _WRONG_IDENTIFIER);
-               return (NULL);
-       }
-       if (!(get_b1_facilities(plci_b, add_b1_facilities(plci_b, plci_b->B1_resource,
-                                                         (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Interconnect peer cannot mix %d",
-                               UnMapId(Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
-               PUT_WORD(p_result, _WRONG_STATE);
-               return (NULL);
-       }
-       return (plci_b);
-}
-
-
-static byte mixer_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
-{
-       word Info;
-       word i;
-       dword d, li_flags, plci_b_id;
-       PLCI *plci_b;
-       API_PARSE li_parms[3];
-       API_PARSE li_req_parms[3];
-       API_PARSE li_participant_struct[2];
-       API_PARSE li_participant_parms[3];
-       word participant_parms_pos;
-       byte result_buffer[32];
-       byte *result;
-       word result_pos;
-       word plci_b_write_pos;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_request",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       Info = GOOD;
-       result = result_buffer;
-       result_buffer[0] = 0;
-       if (!(a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _FACILITY_NOT_SUPPORTED;
-       }
-       else if (api_parse(&msg[1].info[1], msg[1].length, "ws", li_parms))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _WRONG_MESSAGE_FORMAT;
-       }
-       else
-       {
-               result_buffer[0] = 3;
-               PUT_WORD(&result_buffer[1], GET_WORD(li_parms[0].info));
-               result_buffer[3] = 0;
-               switch (GET_WORD(li_parms[0].info))
-               {
-               case LI_GET_SUPPORTED_SERVICES:
-                       if (appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
-                       {
-                               result_buffer[0] = 17;
-                               result_buffer[3] = 14;
-                               PUT_WORD(&result_buffer[4], GOOD);
-                               d = 0;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_CH)
-                                       d |= LI_CONFERENCING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
-                                       d |= LI_MONITORING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
-                                       d |= LI_ANNOUNCEMENTS_SUPPORTED | LI_MIXING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                                       d |= LI_CROSS_CONTROLLER_SUPPORTED;
-                               PUT_DWORD(&result_buffer[6], d);
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                               {
-                                       d = 0;
-                                       for (i = 0; i < li_total_channels; i++)
-                                       {
-                                               if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                                                   && (li_config_table[i].adapter->li_pri
-                                                       || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
-                                               {
-                                                       d++;
-                                               }
-                                       }
-                               }
-                               else
-                               {
-                                       d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
-                               }
-                               PUT_DWORD(&result_buffer[10], d / 2);
-                               PUT_DWORD(&result_buffer[14], d);
-                       }
-                       else
-                       {
-                               result_buffer[0] = 25;
-                               result_buffer[3] = 22;
-                               PUT_WORD(&result_buffer[4], GOOD);
-                               d = LI2_ASYMMETRIC_SUPPORTED | LI2_B_LOOPING_SUPPORTED | LI2_X_LOOPING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
-                                       d |= LI2_MONITORING_SUPPORTED | LI2_REMOTE_MONITORING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
-                                       d |= LI2_MIXING_SUPPORTED | LI2_REMOTE_MIXING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_PC)
-                                       d |= LI2_PC_LOOPING_SUPPORTED;
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                                       d |= LI2_CROSS_CONTROLLER_SUPPORTED;
-                               PUT_DWORD(&result_buffer[6], d);
-                               d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
-                               PUT_DWORD(&result_buffer[10], d / 2);
-                               PUT_DWORD(&result_buffer[14], d - 1);
-                               if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                               {
-                                       d = 0;
-                                       for (i = 0; i < li_total_channels; i++)
-                                       {
-                                               if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
-                                                   && (li_config_table[i].adapter->li_pri
-                                                       || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
-                                               {
-                                                       d++;
-                                               }
-                                       }
-                               }
-                               PUT_DWORD(&result_buffer[18], d / 2);
-                               PUT_DWORD(&result_buffer[22], d - 1);
-                       }
-                       break;
-
-               case LI_REQ_CONNECT:
-                       if (li_parms[1].length == 8)
-                       {
-                               appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
-                               if (api_parse(&li_parms[1].info[1], li_parms[1].length, "dd", li_req_parms))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff;
-                               li_flags = GET_DWORD(li_req_parms[1].info);
-                               Info = li_check_main_plci(Id, plci);
-                               result_buffer[0] = 9;
-                               result_buffer[3] = 6;
-                               PUT_DWORD(&result_buffer[4], plci_b_id);
-                               PUT_WORD(&result_buffer[8], GOOD);
-                               if (Info != GOOD)
-                                       break;
-                               result = plci->saved_msg.info;
-                               for (i = 0; i <= result_buffer[0]; i++)
-                                       result[i] = result_buffer[i];
-                               plci_b_write_pos = plci->li_plci_b_write_pos;
-                               plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
-                               if (plci_b == NULL)
-                                       break;
-                               li_update_connect(Id, a, plci, plci_b_id, true, li_flags);
-                               plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_LAST_FLAG;
-                               plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                               plci->li_plci_b_write_pos = plci_b_write_pos;
-                       }
-                       else
-                       {
-                               appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
-                               if (api_parse(&li_parms[1].info[1], li_parms[1].length, "ds", li_req_parms))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               li_flags = GET_DWORD(li_req_parms[0].info) & ~(LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A);
-                               Info = li_check_main_plci(Id, plci);
-                               result_buffer[0] = 7;
-                               result_buffer[3] = 4;
-                               PUT_WORD(&result_buffer[4], Info);
-                               result_buffer[6] = 0;
-                               if (Info != GOOD)
-                                       break;
-                               result = plci->saved_msg.info;
-                               for (i = 0; i <= result_buffer[0]; i++)
-                                       result[i] = result_buffer[i];
-                               plci_b_write_pos = plci->li_plci_b_write_pos;
-                               participant_parms_pos = 0;
-                               result_pos = 7;
-                               li2_update_connect(Id, a, plci, UnMapId(Id), true, li_flags);
-                               while (participant_parms_pos < li_req_parms[1].length)
-                               {
-                                       result[result_pos] = 6;
-                                       result_pos += 7;
-                                       PUT_DWORD(&result[result_pos - 6], 0);
-                                       PUT_WORD(&result[result_pos - 2], GOOD);
-                                       if (api_parse(&li_req_parms[1].info[1 + participant_parms_pos],
-                                                     (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
-                                               break;
-                                       }
-                                       if (api_parse(&li_participant_struct[0].info[1],
-                                                     li_participant_struct[0].length, "dd", li_participant_parms))
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
-                                               break;
-                                       }
-                                       plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff;
-                                       li_flags = GET_DWORD(li_participant_parms[1].info);
-                                       PUT_DWORD(&result[result_pos - 6], plci_b_id);
-                                       if (sizeof(result) - result_pos < 7)
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: LI result overrun",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_STATE);
-                                               break;
-                                       }
-                                       plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
-                                       if (plci_b != NULL)
-                                       {
-                                               li2_update_connect(Id, a, plci, plci_b_id, true, li_flags);
-                                               plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id |
-                                                       ((li_flags & (LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A |
-                                                                     LI2_FLAG_PCCONNECT_A_B | LI2_FLAG_PCCONNECT_B_A)) ? 0 : LI_PLCI_B_DISC_FLAG);
-                                               plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                                       }
-                                       participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
-                                                                      (&li_req_parms[1].info[1]));
-                               }
-                               result[0] = (byte)(result_pos - 1);
-                               result[3] = (byte)(result_pos - 4);
-                               result[6] = (byte)(result_pos - 7);
-                               i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
-                               if ((plci_b_write_pos == plci->li_plci_b_read_pos)
-                                   || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
-                               {
-                                       plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
-                                       plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                               }
-                               else
-                                       plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
-                               plci->li_plci_b_write_pos = plci_b_write_pos;
-                       }
-                       mixer_calculate_coefs(a);
-                       plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
-                       mixer_notify_update(plci, true);
-                       sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
-                             "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
-                       plci->command = 0;
-                       plci->li_cmd = GET_WORD(li_parms[0].info);
-                       start_internal_command(Id, plci, mixer_command);
-                       return (false);
-
-               case LI_REQ_DISCONNECT:
-                       if (li_parms[1].length == 4)
-                       {
-                               appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
-                               if (api_parse(&li_parms[1].info[1], li_parms[1].length, "d", li_req_parms))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               plci_b_id = GET_DWORD(li_req_parms[0].info) & 0xffff;
-                               Info = li_check_main_plci(Id, plci);
-                               result_buffer[0] = 9;
-                               result_buffer[3] = 6;
-                               PUT_DWORD(&result_buffer[4], GET_DWORD(li_req_parms[0].info));
-                               PUT_WORD(&result_buffer[8], GOOD);
-                               if (Info != GOOD)
-                                       break;
-                               result = plci->saved_msg.info;
-                               for (i = 0; i <= result_buffer[0]; i++)
-                                       result[i] = result_buffer[i];
-                               plci_b_write_pos = plci->li_plci_b_write_pos;
-                               plci_b = li_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
-                               if (plci_b == NULL)
-                                       break;
-                               li_update_connect(Id, a, plci, plci_b_id, false, 0);
-                               plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG | LI_PLCI_B_LAST_FLAG;
-                               plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                               plci->li_plci_b_write_pos = plci_b_write_pos;
-                       }
-                       else
-                       {
-                               appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
-                               if (api_parse(&li_parms[1].info[1], li_parms[1].length, "s", li_req_parms))
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_MESSAGE_FORMAT;
-                                       break;
-                               }
-                               Info = li_check_main_plci(Id, plci);
-                               result_buffer[0] = 7;
-                               result_buffer[3] = 4;
-                               PUT_WORD(&result_buffer[4], Info);
-                               result_buffer[6] = 0;
-                               if (Info != GOOD)
-                                       break;
-                               result = plci->saved_msg.info;
-                               for (i = 0; i <= result_buffer[0]; i++)
-                                       result[i] = result_buffer[i];
-                               plci_b_write_pos = plci->li_plci_b_write_pos;
-                               participant_parms_pos = 0;
-                               result_pos = 7;
-                               while (participant_parms_pos < li_req_parms[0].length)
-                               {
-                                       result[result_pos] = 6;
-                                       result_pos += 7;
-                                       PUT_DWORD(&result[result_pos - 6], 0);
-                                       PUT_WORD(&result[result_pos - 2], GOOD);
-                                       if (api_parse(&li_req_parms[0].info[1 + participant_parms_pos],
-                                                     (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
-                                               break;
-                                       }
-                                       if (api_parse(&li_participant_struct[0].info[1],
-                                                     li_participant_struct[0].length, "d", li_participant_parms))
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
-                                               break;
-                                       }
-                                       plci_b_id = GET_DWORD(li_participant_parms[0].info) & 0xffff;
-                                       PUT_DWORD(&result[result_pos - 6], plci_b_id);
-                                       if (sizeof(result) - result_pos < 7)
-                                       {
-                                               dbug(1, dprintf("[%06lx] %s,%d: LI result overrun",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                                               PUT_WORD(&result[result_pos - 2], _WRONG_STATE);
-                                               break;
-                                       }
-                                       plci_b = li2_check_plci_b(Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
-                                       if (plci_b != NULL)
-                                       {
-                                               li2_update_connect(Id, a, plci, plci_b_id, false, 0);
-                                               plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
-                                               plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                                       }
-                                       participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
-                                                                      (&li_req_parms[0].info[1]));
-                               }
-                               result[0] = (byte)(result_pos - 1);
-                               result[3] = (byte)(result_pos - 4);
-                               result[6] = (byte)(result_pos - 7);
-                               i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
-                               if ((plci_b_write_pos == plci->li_plci_b_read_pos)
-                                   || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
-                               {
-                                       plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
-                                       plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                               }
-                               else
-                                       plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
-                               plci->li_plci_b_write_pos = plci_b_write_pos;
-                       }
-                       mixer_calculate_coefs(a);
-                       plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
-                       mixer_notify_update(plci, true);
-                       sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
-                             "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
-                       plci->command = 0;
-                       plci->li_cmd = GET_WORD(li_parms[0].info);
-                       start_internal_command(Id, plci, mixer_command);
-                       return (false);
-
-               case LI_REQ_SILENT_UPDATE:
-                       if (!plci || !plci->State
-                           || !plci->NL.Id || plci->nl_remove_id
-                           || (plci->li_bchannel_id == 0)
-                           || (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci != plci))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               return (false);
-                       }
-                       plci_b_write_pos = plci->li_plci_b_write_pos;
-                       if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
-                            LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               return (false);
-                       }
-                       i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES - 1 : plci_b_write_pos - 1;
-                       if ((plci_b_write_pos == plci->li_plci_b_read_pos)
-                           || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
-                       {
-                               plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
-                               plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-                       }
-                       else
-                               plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
-                       plci->li_plci_b_write_pos = plci_b_write_pos;
-                       plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
-                       plci->command = 0;
-                       plci->li_cmd = GET_WORD(li_parms[0].info);
-                       start_internal_command(Id, plci, mixer_command);
-                       return (false);
-
-               default:
-                       dbug(1, dprintf("[%06lx] %s,%d: LI unknown request %04x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, GET_WORD(li_parms[0].info)));
-                       Info = _FACILITY_NOT_SUPPORTED;
-               }
-       }
-       sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
-             "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
-       return (false);
-}
-
-
-static void mixer_indication_coefs_set(dword Id, PLCI *plci)
-{
-       dword d;
-       byte result[12];
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_coefs_set",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       if (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos)
-       {
-               do
-               {
-                       d = plci->li_plci_b_queue[plci->li_plci_b_read_pos];
-                       if (!(d & LI_PLCI_B_SKIP_FLAG))
-                       {
-                               if (plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
-                               {
-                                       if (d & LI_PLCI_B_DISC_FLAG)
-                                       {
-                                               result[0] = 5;
-                                               PUT_WORD(&result[1], LI_IND_DISCONNECT);
-                                               result[3] = 2;
-                                               PUT_WORD(&result[4], _LI_USER_INITIATED);
-                                       }
-                                       else
-                                       {
-                                               result[0] = 7;
-                                               PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE);
-                                               result[3] = 4;
-                                               PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
-                                       }
-                               }
-                               else
-                               {
-                                       if (d & LI_PLCI_B_DISC_FLAG)
-                                       {
-                                               result[0] = 9;
-                                               PUT_WORD(&result[1], LI_IND_DISCONNECT);
-                                               result[3] = 6;
-                                               PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
-                                               PUT_WORD(&result[8], _LI_USER_INITIATED);
-                                       }
-                                       else
-                                       {
-                                               result[0] = 7;
-                                               PUT_WORD(&result[1], LI_IND_CONNECT_ACTIVE);
-                                               result[3] = 4;
-                                               PUT_DWORD(&result[4], d & ~LI_PLCI_B_FLAG_MASK);
-                                       }
-                               }
-                               sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,
-                                     "ws", SELECTOR_LINE_INTERCONNECT, result);
-                       }
-                       plci->li_plci_b_read_pos = (plci->li_plci_b_read_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ?
-                               0 : plci->li_plci_b_read_pos + 1;
-               } while (!(d & LI_PLCI_B_LAST_FLAG) && (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos));
-       }
-}
-
-
-static void mixer_indication_xconnect_from(dword Id, PLCI *plci, byte *msg, word length)
-{
-       word i, j, ch;
-       struct xconnect_transfer_address_s s,   *p;
-       DIVA_CAPI_ADAPTER *a;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_from %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, (int)length));
-
-       a = plci->adapter;
-       i = 1;
-       for (i = 1; i < length; i += 16)
-       {
-               s.card_address.low = msg[i] | (msg[i + 1] << 8) | (((dword)(msg[i + 2])) << 16) | (((dword)(msg[i + 3])) << 24);
-               s.card_address.high = msg[i + 4] | (msg[i + 5] << 8) | (((dword)(msg[i + 6])) << 16) | (((dword)(msg[i + 7])) << 24);
-               s.offset = msg[i + 8] | (msg[i + 9] << 8) | (((dword)(msg[i + 10])) << 16) | (((dword)(msg[i + 11])) << 24);
-               ch = msg[i + 12] | (msg[i + 13] << 8);
-               j = ch & XCONNECT_CHANNEL_NUMBER_MASK;
-               if (!a->li_pri && (plci->li_bchannel_id == 2))
-                       j = 1 - j;
-               j += a->li_base;
-               if (ch & XCONNECT_CHANNEL_PORT_PC)
-                       p = &(li_config_table[j].send_pc);
-               else
-                       p = &(li_config_table[j].send_b);
-               p->card_address.low = s.card_address.low;
-               p->card_address.high = s.card_address.high;
-               p->offset = s.offset;
-               li_config_table[j].channel |= LI_CHANNEL_ADDRESSES_SET;
-       }
-       if (plci->internal_command_queue[0]
-           && ((plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
-               || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
-               || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)))
-       {
-               (*(plci->internal_command_queue[0]))(Id, plci, 0);
-               if (!plci->internal_command)
-                       next_internal_command(Id, plci);
-       }
-       mixer_notify_update(plci, true);
-}
-
-
-static void mixer_indication_xconnect_to(dword Id, PLCI *plci, byte *msg, word length)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_indication_xconnect_to %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, (int) length));
-
-}
-
-
-static byte mixer_notify_source_removed(PLCI *plci, dword plci_b_id)
-{
-       word plci_b_write_pos;
-
-       plci_b_write_pos = plci->li_plci_b_write_pos;
-       if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
-            LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 1)
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: LI request overrun",
-                               (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                               (char *)(FILE_), __LINE__));
-               return (false);
-       }
-       plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
-       plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES - 1) ? 0 : plci_b_write_pos + 1;
-       plci->li_plci_b_write_pos = plci_b_write_pos;
-       return (true);
-}
-
-
-static void mixer_remove(PLCI *plci)
-{
-       DIVA_CAPI_ADAPTER *a;
-       PLCI *notify_plci;
-       dword plci_b_id;
-       word i, j;
-
-       dbug(1, dprintf("[%06lx] %s,%d: mixer_remove",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       a = plci->adapter;
-       plci_b_id = (plci->Id << 8) | UnMapController(plci->adapter->Id);
-       if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
-       {
-               if ((plci->li_bchannel_id != 0)
-                   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-               {
-                       i = a->li_base + (plci->li_bchannel_id - 1);
-                       if ((li_config_table[i].curchnl | li_config_table[i].channel) & LI_CHANNEL_INVOLVED)
-                       {
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       if ((li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
-                                           || (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT))
-                                       {
-                                               notify_plci = li_config_table[j].plci;
-                                               if ((notify_plci != NULL)
-                                                   && (notify_plci != plci)
-                                                   && (notify_plci->appl != NULL)
-                                                   && !(notify_plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
-                                                   && (notify_plci->State)
-                                                   && notify_plci->NL.Id && !notify_plci->nl_remove_id)
-                                               {
-                                                       mixer_notify_source_removed(notify_plci, plci_b_id);
-                                               }
-                                       }
-                               }
-                               mixer_clear_config(plci);
-                               mixer_calculate_coefs(a);
-                               mixer_notify_update(plci, true);
-                       }
-                       li_config_table[i].plci = NULL;
-                       plci->li_bchannel_id = 0;
-               }
-       }
-}
-
-
-/*------------------------------------------------------------------*/
-/* Echo canceller facilities                                        */
-/*------------------------------------------------------------------*/
-
-
-static void ec_write_parameters(PLCI *plci)
-{
-       word w;
-       byte parameter_buffer[6];
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_write_parameters",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       parameter_buffer[0] = 5;
-       parameter_buffer[1] = DSP_CTRL_SET_LEC_PARAMETERS;
-       PUT_WORD(&parameter_buffer[2], plci->ec_idi_options);
-       plci->ec_idi_options &= ~LEC_RESET_COEFFICIENTS;
-       w = (plci->ec_tail_length == 0) ? 128 : plci->ec_tail_length;
-       PUT_WORD(&parameter_buffer[4], w);
-       add_p(plci, FTY, parameter_buffer);
-       sig_req(plci, TEL_CTRL, 0);
-       send_req(plci);
-}
-
-
-static void ec_clear_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
-               LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING;
-       plci->ec_tail_length = 0;
-}
-
-
-static void ec_prepare_switch(dword Id, PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_prepare_switch",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-}
-
-
-static word ec_save_config(dword Id, PLCI *plci, byte Rc)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_save_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       return (GOOD);
-}
-
-
-static word ec_restore_config(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_restore_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       if (plci->B1_facilities & B1_FACILITY_EC)
-       {
-               switch (plci->adjust_b_state)
-               {
-               case ADJUST_B_RESTORE_EC_1:
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci->sig_req)
-                       {
-                               plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
-                               break;
-                       }
-                       ec_write_parameters(plci);
-                       plci->adjust_b_state = ADJUST_B_RESTORE_EC_2;
-                       break;
-               case ADJUST_B_RESTORE_EC_2:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Restore EC failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-                       break;
-               }
-       }
-       return (Info);
-}
-
-
-static void ec_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command, Info;
-       byte result[8];
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_command %02x %04x %04x %04x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
-                       plci->ec_cmd, plci->ec_idi_options, plci->ec_tail_length));
-
-       Info = GOOD;
-       if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
-       {
-               result[0] = 2;
-               PUT_WORD(&result[1], EC_SUCCESS);
-       }
-       else
-       {
-               result[0] = 5;
-               PUT_WORD(&result[1], plci->ec_cmd);
-               result[3] = 2;
-               PUT_WORD(&result[4], GOOD);
-       }
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (plci->ec_cmd)
-       {
-       case EC_ENABLE_OPERATION:
-       case EC_FREEZE_COEFFICIENTS:
-       case EC_RESUME_COEFFICIENT_UPDATE:
-       case EC_RESET_COEFFICIENTS:
-               switch (internal_command)
-               {
-               default:
-                       adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities |
-                                                                 B1_FACILITY_EC), EC_COMMAND_1);
-                       /* fall through */
-               case EC_COMMAND_1:
-                       if (adjust_b_process(Id, plci, Rc) != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Load EC failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               return;
-                       /* fall through */
-               case EC_COMMAND_2:
-                       if (plci->sig_req)
-                       {
-                               plci->internal_command = EC_COMMAND_2;
-                               return;
-                       }
-                       plci->internal_command = EC_COMMAND_3;
-                       ec_write_parameters(plci);
-                       return;
-               case EC_COMMAND_3:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Enable EC failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       break;
-               }
-               break;
-
-       case EC_DISABLE_OPERATION:
-               switch (internal_command)
-               {
-               default:
-               case EC_COMMAND_1:
-                       if (plci->B1_facilities & B1_FACILITY_EC)
-                       {
-                               if (plci->sig_req)
-                               {
-                                       plci->internal_command = EC_COMMAND_1;
-                                       return;
-                               }
-                               plci->internal_command = EC_COMMAND_2;
-                               ec_write_parameters(plci);
-                               return;
-                       }
-                       Rc = OK;
-                       /* fall through */
-               case EC_COMMAND_2:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Disable EC failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       adjust_b1_resource(Id, plci, NULL, (word)(plci->B1_facilities &
-                                                                 ~B1_FACILITY_EC), EC_COMMAND_3);
-                       /* fall through */
-               case EC_COMMAND_3:
-                       if (adjust_b_process(Id, plci, Rc) != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Unload EC failed",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _FACILITY_NOT_SUPPORTED;
-                               break;
-                       }
-                       if (plci->internal_command)
-                               return;
-                       break;
-               }
-               break;
-       }
-       sendf(plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
-             "wws", Info, (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
-             PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
-}
-
-
-static byte ec_request(dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL   *appl, API_PARSE *msg)
-{
-       word Info;
-       word opt;
-       API_PARSE ec_parms[3];
-       byte result[16];
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_request",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       Info = GOOD;
-       result[0] = 0;
-       if (!(a->man_profile.private_options & (1L << PRIVATE_ECHO_CANCELLER)))
-       {
-               dbug(1, dprintf("[%06lx] %s,%d: Facility not supported",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               Info = _FACILITY_NOT_SUPPORTED;
-       }
-       else
-       {
-               if (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
-               {
-                       if (api_parse(&msg[1].info[1], msg[1].length, "w", ec_parms))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _WRONG_MESSAGE_FORMAT;
-                       }
-                       else
-                       {
-                               if (plci == NULL)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_IDENTIFIER;
-                               }
-                               else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_STATE;
-                               }
-                               else
-                               {
-                                       plci->command = 0;
-                                       plci->ec_cmd = GET_WORD(ec_parms[0].info);
-                                       plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
-                                       result[0] = 2;
-                                       PUT_WORD(&result[1], EC_SUCCESS);
-                                       if (msg[1].length >= 4)
-                                       {
-                                               opt = GET_WORD(&ec_parms[0].info[2]);
-                                               plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
-                                                                         LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
-                                               if (!(opt & EC_DISABLE_NON_LINEAR_PROCESSING))
-                                                       plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
-                                               if (opt & EC_DETECT_DISABLE_TONE)
-                                                       plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
-                                               if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
-                                                       plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
-                                               if (msg[1].length >= 6)
-                                               {
-                                                       plci->ec_tail_length = GET_WORD(&ec_parms[0].info[4]);
-                                               }
-                                       }
-                                       switch (plci->ec_cmd)
-                                       {
-                                       case EC_ENABLE_OPERATION:
-                                               plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       case EC_DISABLE_OPERATION:
-                                               plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
-                                                       LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
-                                                       LEC_RESET_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       case EC_FREEZE_COEFFICIENTS:
-                                               plci->ec_idi_options |= LEC_FREEZE_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       case EC_RESUME_COEFFICIENT_UPDATE:
-                                               plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       case EC_RESET_COEFFICIENTS:
-                                               plci->ec_idi_options |= LEC_RESET_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       default:
-                                               dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
-                                               PUT_WORD(&result[1], EC_UNSUPPORTED_OPERATION);
-                                       }
-                               }
-                       }
-               }
-               else
-               {
-                       if (api_parse(&msg[1].info[1], msg[1].length, "ws", ec_parms))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Wrong message format",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__));
-                               Info = _WRONG_MESSAGE_FORMAT;
-                       }
-                       else
-                       {
-                               if (GET_WORD(ec_parms[0].info) == EC_GET_SUPPORTED_SERVICES)
-                               {
-                                       result[0] = 11;
-                                       PUT_WORD(&result[1], EC_GET_SUPPORTED_SERVICES);
-                                       result[3] = 8;
-                                       PUT_WORD(&result[4], GOOD);
-                                       PUT_WORD(&result[6], 0x0007);
-                                       PUT_WORD(&result[8], LEC_MAX_SUPPORTED_TAIL_LENGTH);
-                                       PUT_WORD(&result[10], 0);
-                               }
-                               else if (plci == NULL)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong PLCI",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_IDENTIFIER;
-                               }
-                               else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
-                               {
-                                       dbug(1, dprintf("[%06lx] %s,%d: Wrong state",
-                                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                                       Info = _WRONG_STATE;
-                               }
-                               else
-                               {
-                                       plci->command = 0;
-                                       plci->ec_cmd = GET_WORD(ec_parms[0].info);
-                                       plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
-                                       result[0] = 5;
-                                       PUT_WORD(&result[1], plci->ec_cmd);
-                                       result[3] = 2;
-                                       PUT_WORD(&result[4], GOOD);
-                                       plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
-                                                                 LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
-                                       plci->ec_tail_length = 0;
-                                       if (ec_parms[1].length >= 2)
-                                       {
-                                               opt = GET_WORD(&ec_parms[1].info[1]);
-                                               if (opt & EC_ENABLE_NON_LINEAR_PROCESSING)
-                                                       plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
-                                               if (opt & EC_DETECT_DISABLE_TONE)
-                                                       plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
-                                               if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
-                                                       plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
-                                               if (ec_parms[1].length >= 4)
-                                               {
-                                                       plci->ec_tail_length = GET_WORD(&ec_parms[1].info[3]);
-                                               }
-                                       }
-                                       switch (plci->ec_cmd)
-                                       {
-                                       case EC_ENABLE_OPERATION:
-                                               plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       case EC_DISABLE_OPERATION:
-                                               plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
-                                                       LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
-                                                       LEC_RESET_COEFFICIENTS;
-                                               start_internal_command(Id, plci, ec_command);
-                                               return (false);
-
-                                       default:
-                                               dbug(1, dprintf("[%06lx] %s,%d: EC unknown request %04x",
-                                                               UnMapId(Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
-                                               PUT_WORD(&result[4], _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP);
-                                       }
-                               }
-                       }
-               }
-       }
-       sendf(appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
-             "wws", Info, (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
-             PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
-       return (false);
-}
-
-
-static void ec_indication(dword Id, PLCI *plci, byte *msg, word length)
-{
-       byte result[8];
-
-       dbug(1, dprintf("[%06lx] %s,%d: ec_indication",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-       if (!(plci->ec_idi_options & LEC_MANUAL_DISABLE))
-       {
-               if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
-               {
-                       result[0] = 2;
-                       PUT_WORD(&result[1], 0);
-                       switch (msg[1])
-                       {
-                       case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
-                               PUT_WORD(&result[1], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
-                               break;
-                       case LEC_DISABLE_TYPE_REVERSED_2100HZ:
-                               PUT_WORD(&result[1], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
-                               break;
-                       case LEC_DISABLE_RELEASED:
-                               PUT_WORD(&result[1], EC_BYPASS_RELEASED);
-                               break;
-                       }
-               }
-               else
-               {
-                       result[0] = 5;
-                       PUT_WORD(&result[1], EC_BYPASS_INDICATION);
-                       result[3] = 2;
-                       PUT_WORD(&result[4], 0);
-                       switch (msg[1])
-                       {
-                       case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
-                               PUT_WORD(&result[4], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
-                               break;
-                       case LEC_DISABLE_TYPE_REVERSED_2100HZ:
-                               PUT_WORD(&result[4], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
-                               break;
-                       case LEC_DISABLE_RELEASED:
-                               PUT_WORD(&result[4], EC_BYPASS_RELEASED);
-                               break;
-                       }
-               }
-               sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
-                     PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
-       }
-}
-
-
-
-/*------------------------------------------------------------------*/
-/* Advanced voice                                                   */
-/*------------------------------------------------------------------*/
-
-static void adv_voice_write_coefs(PLCI *plci, word write_command)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word i;
-       byte *p;
-
-       word w, n, j, k;
-       byte ch_map[MIXER_CHANNELS_BRI];
-
-       byte coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE + 2];
-
-       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_write_coefs %d",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, write_command));
-
-       a = plci->adapter;
-       p = coef_buffer + 1;
-       *(p++) = DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS;
-       i = 0;
-       while (i + sizeof(word) <= a->adv_voice_coef_length)
-       {
-               PUT_WORD(p, GET_WORD(a->adv_voice_coef_buffer + i));
-               p += 2;
-               i += 2;
-       }
-       while (i < ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
-       {
-               PUT_WORD(p, 0x8000);
-               p += 2;
-               i += 2;
-       }
-
-       if (!a->li_pri && (plci->li_bchannel_id == 0))
-       {
-               if ((li_config_table[a->li_base].plci == NULL) && (li_config_table[a->li_base + 1].plci != NULL))
-               {
-                       plci->li_bchannel_id = 1;
-                       li_config_table[a->li_base].plci = plci;
-                       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
-                                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                                       (char *)(FILE_), __LINE__, plci->li_bchannel_id));
-               }
-               else if ((li_config_table[a->li_base].plci != NULL) && (li_config_table[a->li_base + 1].plci == NULL))
-               {
-                       plci->li_bchannel_id = 2;
-                       li_config_table[a->li_base + 1].plci = plci;
-                       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
-                                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                                       (char *)(FILE_), __LINE__, plci->li_bchannel_id));
-               }
-       }
-       if (!a->li_pri && (plci->li_bchannel_id != 0)
-           && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               i = a->li_base + (plci->li_bchannel_id - 1);
-               switch (write_command)
-               {
-               case ADV_VOICE_WRITE_ACTIVATION:
-                       j = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
-                       k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
-                       if (!(plci->B1_facilities & B1_FACILITY_MIXER))
-                       {
-                               li_config_table[j].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
-                               li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
-                       }
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                       {
-                               li_config_table[k].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
-                               li_config_table[i].flag_table[k] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
-                               li_config_table[k].flag_table[j] |= LI_FLAG_CONFERENCE;
-                               li_config_table[j].flag_table[k] |= LI_FLAG_CONFERENCE;
-                       }
-                       mixer_calculate_coefs(a);
-                       li_config_table[i].curchnl = li_config_table[i].channel;
-                       li_config_table[j].curchnl = li_config_table[j].channel;
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                               li_config_table[k].curchnl = li_config_table[k].channel;
-                       break;
-
-               case ADV_VOICE_WRITE_DEACTIVATION:
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               li_config_table[i].flag_table[j] = 0;
-                               li_config_table[j].flag_table[i] = 0;
-                       }
-                       k = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               li_config_table[k].flag_table[j] = 0;
-                               li_config_table[j].flag_table[k] = 0;
-                       }
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                       {
-                               k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       li_config_table[k].flag_table[j] = 0;
-                                       li_config_table[j].flag_table[k] = 0;
-                               }
-                       }
-                       mixer_calculate_coefs(a);
-                       break;
-               }
-               if (plci->B1_facilities & B1_FACILITY_MIXER)
-               {
-                       w = 0;
-                       if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)
-                               w = GET_WORD(a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
-                       if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
-                               w |= MIXER_FEATURE_ENABLE_TX_DATA;
-                       if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
-                               w |= MIXER_FEATURE_ENABLE_RX_DATA;
-                       *(p++) = (byte) w;
-                       *(p++) = (byte)(w >> 8);
-                       for (j = 0; j < sizeof(ch_map); j += 2)
-                       {
-                               ch_map[j] = (byte)(j + (plci->li_bchannel_id - 1));
-                               ch_map[j + 1] = (byte)(j + (2 - plci->li_bchannel_id));
-                       }
-                       for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
-                       {
-                               i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
-                               j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
-                               if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
-                               {
-                                       *(p++) = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
-                                       w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
-                                       li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
-                               }
-                               else
-                               {
-                                       *(p++) = (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n < a->adv_voice_coef_length) ?
-                                               a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n] : 0x00;
-                               }
-                       }
-               }
-               else
-               {
-                       for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
-                               *(p++) = a->adv_voice_coef_buffer[i];
-               }
-       }
-       else
-
-       {
-               for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
-                       *(p++) = a->adv_voice_coef_buffer[i];
-       }
-       coef_buffer[0] = (p - coef_buffer) - 1;
-       add_p(plci, FTY, coef_buffer);
-       sig_req(plci, TEL_CTRL, 0);
-       send_req(plci);
-}
-
-
-static void adv_voice_clear_config(PLCI *plci)
-{
-       DIVA_CAPI_ADAPTER *a;
-
-       word i, j;
-
-
-       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_clear_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       a = plci->adapter;
-       if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
-       {
-               a->adv_voice_coef_length = 0;
-
-               if (!a->li_pri && (plci->li_bchannel_id != 0)
-                   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-               {
-                       i = a->li_base + (plci->li_bchannel_id - 1);
-                       li_config_table[i].curchnl = 0;
-                       li_config_table[i].channel = 0;
-                       li_config_table[i].chflags = 0;
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               li_config_table[i].flag_table[j] = 0;
-                               li_config_table[j].flag_table[i] = 0;
-                               li_config_table[i].coef_table[j] = 0;
-                               li_config_table[j].coef_table[i] = 0;
-                       }
-                       li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
-                       i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
-                       li_config_table[i].curchnl = 0;
-                       li_config_table[i].channel = 0;
-                       li_config_table[i].chflags = 0;
-                       for (j = 0; j < li_total_channels; j++)
-                       {
-                               li_config_table[i].flag_table[j] = 0;
-                               li_config_table[j].flag_table[i] = 0;
-                               li_config_table[i].coef_table[j] = 0;
-                               li_config_table[j].coef_table[i] = 0;
-                       }
-                       if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
-                       {
-                               i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
-                               li_config_table[i].curchnl = 0;
-                               li_config_table[i].channel = 0;
-                               li_config_table[i].chflags = 0;
-                               for (j = 0; j < li_total_channels; j++)
-                               {
-                                       li_config_table[i].flag_table[j] = 0;
-                                       li_config_table[j].flag_table[i] = 0;
-                                       li_config_table[i].coef_table[j] = 0;
-                                       li_config_table[j].coef_table[i] = 0;
-                               }
-                       }
-               }
-
-       }
-}
-
-
-static void adv_voice_prepare_switch(dword Id, PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_prepare_switch",
-                       UnMapId(Id), (char *)(FILE_), __LINE__));
-
-}
-
-
-static word adv_voice_save_config(dword Id, PLCI *plci, byte Rc)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_save_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       return (GOOD);
-}
-
-
-static word adv_voice_restore_config(dword Id, PLCI *plci, byte Rc)
-{
-       DIVA_CAPI_ADAPTER *a;
-       word Info;
-
-       dbug(1, dprintf("[%06lx] %s,%d: adv_voice_restore_config %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       a = plci->adapter;
-       if ((plci->B1_facilities & B1_FACILITY_VOICE)
-           && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
-       {
-               switch (plci->adjust_b_state)
-               {
-               case ADJUST_B_RESTORE_VOICE_1:
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci->sig_req)
-                       {
-                               plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
-                               break;
-                       }
-                       adv_voice_write_coefs(plci, ADV_VOICE_WRITE_UPDATE);
-                       plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_2;
-                       break;
-               case ADJUST_B_RESTORE_VOICE_2:
-                       if ((Rc != OK) && (Rc != OK_FC))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Restore voice config failed %02x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-                       break;
-               }
-       }
-       return (Info);
-}
-
-
-
-
-/*------------------------------------------------------------------*/
-/* B1 resource switching                                            */
-/*------------------------------------------------------------------*/
-
-static byte b1_facilities_table[] =
-{
-       0x00,  /* 0  No bchannel resources      */
-       0x00,  /* 1  Codec (automatic law)      */
-       0x00,  /* 2  Codec (A-law)              */
-       0x00,  /* 3  Codec (y-law)              */
-       0x00,  /* 4  HDLC for X.21              */
-       0x00,  /* 5  HDLC                       */
-       0x00,  /* 6  External Device 0          */
-       0x00,  /* 7  External Device 1          */
-       0x00,  /* 8  HDLC 56k                   */
-       0x00,  /* 9  Transparent                */
-       0x00,  /* 10 Loopback to network        */
-       0x00,  /* 11 Test pattern to net        */
-       0x00,  /* 12 Rate adaptation sync       */
-       0x00,  /* 13 Rate adaptation async      */
-       0x00,  /* 14 R-Interface                */
-       0x00,  /* 15 HDLC 128k leased line      */
-       0x00,  /* 16 FAX                        */
-       0x00,  /* 17 Modem async                */
-       0x00,  /* 18 Modem sync HDLC            */
-       0x00,  /* 19 V.110 async HDLC           */
-       0x12,  /* 20 Adv voice (Trans,mixer)    */
-       0x00,  /* 21 Codec connected to IC      */
-       0x0c,  /* 22 Trans,DTMF                 */
-       0x1e,  /* 23 Trans,DTMF+mixer           */
-       0x1f,  /* 24 Trans,DTMF+mixer+local     */
-       0x13,  /* 25 Trans,mixer+local          */
-       0x12,  /* 26 HDLC,mixer                 */
-       0x12,  /* 27 HDLC 56k,mixer             */
-       0x2c,  /* 28 Trans,LEC+DTMF             */
-       0x3e,  /* 29 Trans,LEC+DTMF+mixer       */
-       0x3f,  /* 30 Trans,LEC+DTMF+mixer+local */
-       0x2c,  /* 31 RTP,LEC+DTMF               */
-       0x3e,  /* 32 RTP,LEC+DTMF+mixer         */
-       0x3f,  /* 33 RTP,LEC+DTMF+mixer+local   */
-       0x00,  /* 34 Signaling task             */
-       0x00,  /* 35 PIAFS                      */
-       0x0c,  /* 36 Trans,DTMF+TONE            */
-       0x1e,  /* 37 Trans,DTMF+TONE+mixer      */
-       0x1f   /* 38 Trans,DTMF+TONE+mixer+local*/
-};
-
-
-static word get_b1_facilities(PLCI *plci, byte b1_resource)
-{
-       word b1_facilities;
-
-       b1_facilities = b1_facilities_table[b1_resource];
-       if ((b1_resource == 9) || (b1_resource == 20) || (b1_resource == 25))
-       {
-
-               if (!(((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
-                     || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE)))))
-
-               {
-                       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND)
-                               b1_facilities |= B1_FACILITY_DTMFX;
-                       if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)
-                               b1_facilities |= B1_FACILITY_DTMFR;
-               }
-       }
-       if ((b1_resource == 17) || (b1_resource == 18))
-       {
-               if (plci->adapter->manufacturer_features & (MANUFACTURER_FEATURE_V18 | MANUFACTURER_FEATURE_VOWN))
-                       b1_facilities |= B1_FACILITY_DTMFX | B1_FACILITY_DTMFR;
-       }
-/*
-  dbug (1, dprintf("[%06lx] %s,%d: get_b1_facilities %d %04x",
-  (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-  (char far *)(FILE_), __LINE__, b1_resource, b1_facilites));
-*/
-       return (b1_facilities);
-}
-
-
-static byte add_b1_facilities(PLCI *plci, byte b1_resource, word b1_facilities)
-{
-       byte b;
-
-       switch (b1_resource)
-       {
-       case 5:
-       case 26:
-               if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                       b = 26;
-               else
-                       b = 5;
-               break;
-
-       case 8:
-       case 27:
-               if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                       b = 27;
-               else
-                       b = 8;
-               break;
-
-       case 9:
-       case 20:
-       case 22:
-       case 23:
-       case 24:
-       case 25:
-       case 28:
-       case 29:
-       case 30:
-       case 36:
-       case 37:
-       case 38:
-               if (b1_facilities & B1_FACILITY_EC)
-               {
-                       if (b1_facilities & B1_FACILITY_LOCAL)
-                               b = 30;
-                       else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                               b = 29;
-                       else
-                               b = 28;
-               }
-
-               else if ((b1_facilities & (B1_FACILITY_DTMFX | B1_FACILITY_DTMFR | B1_FACILITY_MIXER))
-                        && (((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
-                            || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id - 1] & (1L << PRIVATE_DTMF_TONE)))))
-               {
-                       if (b1_facilities & B1_FACILITY_LOCAL)
-                               b = 38;
-                       else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                               b = 37;
-                       else
-                               b = 36;
-               }
-
-               else if (((plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
-                         && !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
-                        || ((b1_facilities & B1_FACILITY_DTMFR)
-                            && ((b1_facilities & B1_FACILITY_MIXER)
-                                || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)))
-                        || ((b1_facilities & B1_FACILITY_DTMFX)
-                            && ((b1_facilities & B1_FACILITY_MIXER)
-                                || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND))))
-               {
-                       if (b1_facilities & B1_FACILITY_LOCAL)
-                               b = 24;
-                       else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                               b = 23;
-                       else
-                               b = 22;
-               }
-               else
-               {
-                       if (b1_facilities & B1_FACILITY_LOCAL)
-                               b = 25;
-                       else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                               b = 20;
-                       else
-                               b = 9;
-               }
-               break;
-
-       case 31:
-       case 32:
-       case 33:
-               if (b1_facilities & B1_FACILITY_LOCAL)
-                       b = 33;
-               else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
-                       b = 32;
-               else
-                       b = 31;
-               break;
-
-       default:
-               b = b1_resource;
-       }
-       dbug(1, dprintf("[%06lx] %s,%d: add_b1_facilities %d %04x %d %04x",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__,
-                       b1_resource, b1_facilities, b, get_b1_facilities(plci, b)));
-       return (b);
-}
-
-
-static void adjust_b1_facilities(PLCI *plci, byte new_b1_resource, word new_b1_facilities)
-{
-       word removed_facilities;
-
-       dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_facilities %d %04x %04x",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__, new_b1_resource, new_b1_facilities,
-                       new_b1_facilities & get_b1_facilities(plci, new_b1_resource)));
-
-       new_b1_facilities &= get_b1_facilities(plci, new_b1_resource);
-       removed_facilities = plci->B1_facilities & ~new_b1_facilities;
-
-       if (removed_facilities & B1_FACILITY_EC)
-               ec_clear_config(plci);
-
-
-       if (removed_facilities & B1_FACILITY_DTMFR)
-       {
-               dtmf_rec_clear_config(plci);
-               dtmf_parameter_clear_config(plci);
-       }
-       if (removed_facilities & B1_FACILITY_DTMFX)
-               dtmf_send_clear_config(plci);
-
-
-       if (removed_facilities & B1_FACILITY_MIXER)
-               mixer_clear_config(plci);
-
-       if (removed_facilities & B1_FACILITY_VOICE)
-               adv_voice_clear_config(plci);
-       plci->B1_facilities = new_b1_facilities;
-}
-
-
-static void adjust_b_clear(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: adjust_b_clear",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->adjust_b_restore = false;
-}
-
-
-static word adjust_b_process(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       byte b1_resource;
-       NCCI *ncci_ptr;
-       API_PARSE bp[2];
-
-       dbug(1, dprintf("[%06lx] %s,%d: adjust_b_process %02x %d",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
-
-       Info = GOOD;
-       switch (plci->adjust_b_state)
-       {
-       case ADJUST_B_START:
-               if ((plci->adjust_b_parms_msg == NULL)
-                   && (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
-                   && ((plci->adjust_b_mode & ~(ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 |
-                                                ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_RESTORE)) == 0))
-               {
-                       b1_resource = (plci->adjust_b_mode == ADJUST_B_MODE_NO_RESOURCE) ?
-                               0 : add_b1_facilities(plci, plci->B1_resource, plci->adjust_b_facilities);
-                       if (b1_resource == plci->B1_resource)
-                       {
-                               adjust_b1_facilities(plci, b1_resource, plci->adjust_b_facilities);
-                               break;
-                       }
-                       if (plci->adjust_b_facilities & ~get_b1_facilities(plci, b1_resource))
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Adjust B nonsupported facilities %d %d %04x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__,
-                                               plci->B1_resource, b1_resource, plci->adjust_b_facilities));
-                               Info = _WRONG_STATE;
-                               break;
-                       }
-               }
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-
-                       mixer_prepare_switch(Id, plci);
-
-
-                       dtmf_prepare_switch(Id, plci);
-                       dtmf_parameter_prepare_switch(Id, plci);
-
-
-                       ec_prepare_switch(Id, plci);
-
-                       adv_voice_prepare_switch(Id, plci);
-               }
-               plci->adjust_b_state = ADJUST_B_SAVE_MIXER_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SAVE_MIXER_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-
-                       Info = mixer_save_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_SAVE_DTMF_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SAVE_DTMF_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-
-                       Info = dtmf_save_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_REMOVE_L23_1;
-               /* fall through */
-       case ADJUST_B_REMOVE_L23_1:
-               if ((plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
-                   && plci->NL.Id && !plci->nl_remove_id)
-               {
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci->adjust_b_ncci != 0)
-                       {
-                               ncci_ptr = &(plci->adapter->ncci[plci->adjust_b_ncci]);
-                               while (ncci_ptr->data_pending)
-                               {
-                                       plci->data_sent_ptr = ncci_ptr->DBuffer[ncci_ptr->data_out].P;
-                                       data_rc(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
-                               }
-                               while (ncci_ptr->data_ack_pending)
-                                       data_ack(plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
-                       }
-                       nl_req_ncci(plci, REMOVE,
-                                   (byte)((plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) ? plci->adjust_b_ncci : 0));
-                       send_req(plci);
-                       plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_REMOVE_L23_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B remove failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               if (plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
-               {
-                       if (plci_nl_busy(plci))
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               break;
-                       }
-               }
-               plci->adjust_b_state = ADJUST_B_SAVE_EC_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SAVE_EC_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-
-                       Info = ec_save_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_SAVE_DTMF_PARAMETER_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SAVE_DTMF_PARAMETER_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-
-                       Info = dtmf_parameter_save_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_SAVE_VOICE_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SAVE_VOICE_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
-               {
-                       Info = adv_voice_save_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-               }
-               plci->adjust_b_state = ADJUST_B_SWITCH_L1_1;
-               /* fall through */
-       case ADJUST_B_SWITCH_L1_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
-               {
-                       if (plci->sig_req)
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               break;
-                       }
-                       if (plci->adjust_b_parms_msg != NULL)
-                               api_load_msg(plci->adjust_b_parms_msg, bp);
-                       else
-                               api_load_msg(&plci->B_protocol, bp);
-                       Info = add_b1(plci, bp,
-                                     (word)((plci->adjust_b_mode & ADJUST_B_MODE_NO_RESOURCE) ? 2 : 0),
-                                     plci->adjust_b_facilities);
-                       if (Info != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L1 parameters %d %04x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__,
-                                               plci->B1_resource, plci->adjust_b_facilities));
-                               break;
-                       }
-                       plci->internal_command = plci->adjust_b_command;
-                       sig_req(plci, RESOURCES, 0);
-                       send_req(plci);
-                       plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_SWITCH_L1_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B switch failed %02x %d %04x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__,
-                                       Rc, plci->B1_resource, plci->adjust_b_facilities));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_VOICE_1:
-       case ADJUST_B_RESTORE_VOICE_2:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
-               {
-                       Info = adv_voice_restore_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-               }
-               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
-       case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
-               {
-
-                       Info = dtmf_parameter_restore_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_EC_1:
-       case ADJUST_B_RESTORE_EC_2:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
-               {
-
-                       Info = ec_restore_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_ASSIGN_L23_1;
-               /* fall through */
-       case ADJUST_B_ASSIGN_L23_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
-               {
-                       if (plci_nl_busy(plci))
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               break;
-                       }
-                       if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
-                               plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
-                       if (plci->adjust_b_parms_msg != NULL)
-                               api_load_msg(plci->adjust_b_parms_msg, bp);
-                       else
-                               api_load_msg(&plci->B_protocol, bp);
-                       Info = add_b23(plci, bp);
-                       if (Info != GOOD)
-                       {
-                               dbug(1, dprintf("[%06lx] %s,%d: Adjust B invalid L23 parameters %04x",
-                                               UnMapId(Id), (char *)(FILE_), __LINE__, Info));
-                               break;
-                       }
-                       plci->internal_command = plci->adjust_b_command;
-                       nl_req_ncci(plci, ASSIGN, 0);
-                       send_req(plci);
-                       plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
-               Rc = ASSIGN_OK;
-               /* fall through */
-       case ADJUST_B_ASSIGN_L23_2:
-               if ((Rc != OK) && (Rc != OK_FC) && (Rc != ASSIGN_OK))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B assign failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
-               {
-                       if (Rc != ASSIGN_OK)
-                       {
-                               plci->internal_command = plci->adjust_b_command;
-                               break;
-                       }
-               }
-               if (plci->adjust_b_mode & ADJUST_B_MODE_USER_CONNECT)
-               {
-                       plci->adjust_b_restore = true;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_CONNECT_1;
-               /* fall through */
-       case ADJUST_B_CONNECT_1:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
-               {
-                       plci->internal_command = plci->adjust_b_command;
-                       if (plci_nl_busy(plci))
-                               break;
-                       nl_req_ncci(plci, N_CONNECT, 0);
-                       send_req(plci);
-                       plci->adjust_b_state = ADJUST_B_CONNECT_2;
-                       break;
-               }
-               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_CONNECT_2:
-       case ADJUST_B_CONNECT_3:
-       case ADJUST_B_CONNECT_4:
-               if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B connect failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               if (Rc == OK)
-               {
-                       if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
-                       {
-                               get_ncci(plci, (byte)(Id >> 16), plci->adjust_b_ncci);
-                               Id = (Id & 0xffff) | (((dword)(plci->adjust_b_ncci)) << 16);
-                       }
-                       if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
-                               plci->adjust_b_state = ADJUST_B_CONNECT_3;
-                       else if (plci->adjust_b_state == ADJUST_B_CONNECT_4)
-                               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
-               }
-               else if (Rc == 0)
-               {
-                       if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
-                               plci->adjust_b_state = ADJUST_B_CONNECT_4;
-                       else if (plci->adjust_b_state == ADJUST_B_CONNECT_3)
-                               plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
-               }
-               if (plci->adjust_b_state != ADJUST_B_RESTORE_DTMF_1)
-               {
-                       plci->internal_command = plci->adjust_b_command;
-                       break;
-               }
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_DTMF_1:
-       case ADJUST_B_RESTORE_DTMF_2:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
-               {
-
-                       Info = dtmf_restore_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_MIXER_1:
-       case ADJUST_B_RESTORE_MIXER_2:
-       case ADJUST_B_RESTORE_MIXER_3:
-       case ADJUST_B_RESTORE_MIXER_4:
-       case ADJUST_B_RESTORE_MIXER_5:
-       case ADJUST_B_RESTORE_MIXER_6:
-       case ADJUST_B_RESTORE_MIXER_7:
-               if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
-               {
-
-                       Info = mixer_restore_config(Id, plci, Rc);
-                       if ((Info != GOOD) || plci->internal_command)
-                               break;
-
-               }
-               plci->adjust_b_state = ADJUST_B_END;
-       case ADJUST_B_END:
-               break;
-       }
-       return (Info);
-}
-
-
-static void adjust_b1_resource(dword Id, PLCI *plci, API_SAVE   *bp_msg, word b1_facilities, word internal_command)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: adjust_b1_resource %d %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__,
-                       plci->B1_resource, b1_facilities));
-
-       plci->adjust_b_parms_msg = bp_msg;
-       plci->adjust_b_facilities = b1_facilities;
-       plci->adjust_b_command = internal_command;
-       plci->adjust_b_ncci = (word)(Id >> 16);
-       if ((bp_msg == NULL) && (plci->B1_resource == 0))
-               plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_SWITCH_L1;
-       else
-               plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | ADJUST_B_MODE_RESTORE;
-       plci->adjust_b_state = ADJUST_B_START;
-       dbug(1, dprintf("[%06lx] %s,%d: Adjust B1 resource %d %04x...",
-                       UnMapId(Id), (char *)(FILE_), __LINE__,
-                       plci->B1_resource, b1_facilities));
-}
-
-
-static void adjust_b_restore(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: adjust_b_restore %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               if (plci->req_in != 0)
-               {
-                       plci->internal_command = ADJUST_B_RESTORE_1;
-                       break;
-               }
-               Rc = OK;
-               /* fall through */
-       case ADJUST_B_RESTORE_1:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B enqueued failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-               }
-               plci->adjust_b_parms_msg = NULL;
-               plci->adjust_b_facilities = plci->B1_facilities;
-               plci->adjust_b_command = ADJUST_B_RESTORE_2;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               plci->adjust_b_mode = ADJUST_B_MODE_RESTORE;
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case ADJUST_B_RESTORE_2:
-               if (adjust_b_process(Id, plci, Rc) != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Adjust B restore failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-               }
-               if (plci->internal_command)
-                       break;
-               break;
-       }
-}
-
-
-static void reset_b3_command(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: reset_b3_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               plci->adjust_b_parms_msg = NULL;
-               plci->adjust_b_facilities = plci->B1_facilities;
-               plci->adjust_b_command = RESET_B3_COMMAND_1;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_CONNECT;
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: Reset B3...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case RESET_B3_COMMAND_1:
-               Info = adjust_b_process(Id, plci, Rc);
-               if (Info != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Reset failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       break;
-               }
-               if (plci->internal_command)
-                       return;
-               break;
-       }
-/*  sendf (plci->appl, _RESET_B3_R | CONFIRM, Id, plci->number, "w", Info);*/
-       sendf(plci->appl, _RESET_B3_I, Id, 0, "s", "");
-}
-
-
-static void select_b_command(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       word internal_command;
-       byte esc_chi[3];
-
-       dbug(1, dprintf("[%06lx] %s,%d: select_b_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               plci->adjust_b_parms_msg = &plci->saved_msg;
-               if ((plci->tel == ADV_VOICE) && (plci == plci->adapter->AdvSignalPLCI))
-                       plci->adjust_b_facilities = plci->B1_facilities | B1_FACILITY_VOICE;
-               else
-                       plci->adjust_b_facilities = plci->B1_facilities & ~B1_FACILITY_VOICE;
-               plci->adjust_b_command = SELECT_B_COMMAND_1;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               if (plci->saved_msg.parms[0].length == 0)
-               {
-                       plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
-                               ADJUST_B_MODE_NO_RESOURCE;
-               }
-               else
-               {
-                       plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
-                               ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
-               }
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: Select B protocol...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case SELECT_B_COMMAND_1:
-               Info = adjust_b_process(Id, plci, Rc);
-               if (Info != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: Select B protocol failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       break;
-               }
-               if (plci->internal_command)
-                       return;
-               if (plci->tel == ADV_VOICE)
-               {
-                       esc_chi[0] = 0x02;
-                       esc_chi[1] = 0x18;
-                       esc_chi[2] = plci->b_channel;
-                       SetVoiceChannel(plci->adapter->AdvCodecPLCI, esc_chi, plci->adapter);
-               }
-               break;
-       }
-       sendf(plci->appl, _SELECT_B_REQ | CONFIRM, Id, plci->number, "w", Info);
-}
-
-
-static void fax_connect_ack_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: fax_connect_ack_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0; /* fall through */
-       case FAX_CONNECT_ACK_COMMAND_1:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = FAX_CONNECT_ACK_COMMAND_1;
-                       return;
-               }
-               plci->internal_command = FAX_CONNECT_ACK_COMMAND_2;
-               plci->NData[0].P = plci->fax_connect_info_buffer;
-               plci->NData[0].PLength = plci->fax_connect_info_length;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_CONNECT_ACK;
-               plci->adapter->request(&plci->NL);
-               return;
-       case FAX_CONNECT_ACK_COMMAND_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: FAX issue CONNECT ACK failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       break;
-               }
-       }
-       if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
-           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
-       {
-               if (plci->B3_prot == 4)
-                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-               else
-                       sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "S", plci->ncpi_buffer);
-               plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
-       }
-}
-
-
-static void fax_edata_ack_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: fax_edata_ack_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               /* fall through */
-       case FAX_EDATA_ACK_COMMAND_1:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = FAX_EDATA_ACK_COMMAND_1;
-                       return;
-               }
-               plci->internal_command = FAX_EDATA_ACK_COMMAND_2;
-               plci->NData[0].P = plci->fax_connect_info_buffer;
-               plci->NData[0].PLength = plci->fax_edata_ack_length;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_EDATA;
-               plci->adapter->request(&plci->NL);
-               return;
-       case FAX_EDATA_ACK_COMMAND_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: FAX issue EDATA ACK failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       break;
-               }
-       }
-}
-
-
-static void fax_connect_info_command(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: fax_connect_info_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0; /* fall through */
-       case FAX_CONNECT_INFO_COMMAND_1:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = FAX_CONNECT_INFO_COMMAND_1;
-                       return;
-               }
-               plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
-               plci->NData[0].P = plci->fax_connect_info_buffer;
-               plci->NData[0].PLength = plci->fax_connect_info_length;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_EDATA;
-               plci->adapter->request(&plci->NL);
-               return;
-       case FAX_CONNECT_INFO_COMMAND_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: FAX setting connect info failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
-                       return;
-               }
-               plci->command = _CONNECT_B3_R;
-               nl_req_ncci(plci, N_CONNECT, 0);
-               send_req(plci);
-               return;
-       }
-       sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
-}
-
-
-static void fax_adjust_b23_command(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: fax_adjust_b23_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               plci->adjust_b_parms_msg = NULL;
-               plci->adjust_b_facilities = plci->B1_facilities;
-               plci->adjust_b_command = FAX_ADJUST_B23_COMMAND_1;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23;
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: FAX adjust B23...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case FAX_ADJUST_B23_COMMAND_1:
-               Info = adjust_b_process(Id, plci, Rc);
-               if (Info != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: FAX adjust failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       break;
-               }
-               if (plci->internal_command)
-                       return;
-               /* fall through */
-       case FAX_ADJUST_B23_COMMAND_2:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = FAX_ADJUST_B23_COMMAND_2;
-                       return;
-               }
-               plci->command = _CONNECT_B3_R;
-               nl_req_ncci(plci, N_CONNECT, 0);
-               send_req(plci);
-               return;
-       }
-       sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
-}
-
-
-static void fax_disconnect_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: fax_disconnect_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               plci->internal_command = FAX_DISCONNECT_COMMAND_1;
-               return;
-       case FAX_DISCONNECT_COMMAND_1:
-       case FAX_DISCONNECT_COMMAND_2:
-       case FAX_DISCONNECT_COMMAND_3:
-               if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: FAX disconnect EDATA failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       break;
-               }
-               if (Rc == OK)
-               {
-                       if ((internal_command == FAX_DISCONNECT_COMMAND_1)
-                           || (internal_command == FAX_DISCONNECT_COMMAND_2))
-                       {
-                               plci->internal_command = FAX_DISCONNECT_COMMAND_2;
-                       }
-               }
-               else if (Rc == 0)
-               {
-                       if (internal_command == FAX_DISCONNECT_COMMAND_1)
-                               plci->internal_command = FAX_DISCONNECT_COMMAND_3;
-               }
-               return;
-       }
-}
-
-
-
-static void rtp_connect_b3_req_command(dword Id, PLCI *plci, byte Rc)
-{
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_req_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0; /* fall through */
-       case RTP_CONNECT_B3_REQ_COMMAND_1:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_1;
-                       return;
-               }
-               plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
-               nl_req_ncci(plci, N_CONNECT, 0);
-               send_req(plci);
-               return;
-       case RTP_CONNECT_B3_REQ_COMMAND_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect info failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       Info = _WRONG_STATE;
-                       break;
-               }
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
-                       return;
-               }
-               plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_3;
-               plci->NData[0].PLength = plci->internal_req_buffer[0];
-               plci->NData[0].P = plci->internal_req_buffer + 1;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-               plci->adapter->request(&plci->NL);
-               break;
-       case RTP_CONNECT_B3_REQ_COMMAND_3:
-               return;
-       }
-       sendf(plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
-}
-
-
-static void rtp_connect_b3_res_command(dword Id, PLCI *plci, byte Rc)
-{
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: rtp_connect_b3_res_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0; /* fall through */
-       case RTP_CONNECT_B3_RES_COMMAND_1:
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_1;
-                       return;
-               }
-               plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
-               nl_req_ncci(plci, N_CONNECT_ACK, (byte)(Id >> 16));
-               send_req(plci);
-               return;
-       case RTP_CONNECT_B3_RES_COMMAND_2:
-               if ((Rc != OK) && (Rc != OK_FC))
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: RTP setting connect resp info failed %02x",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc));
-                       break;
-               }
-               if (plci_nl_busy(plci))
-               {
-                       plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
-                       return;
-               }
-               sendf(plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
-               plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_3;
-               plci->NData[0].PLength = plci->internal_req_buffer[0];
-               plci->NData[0].P = plci->internal_req_buffer + 1;
-               plci->NL.X = plci->NData;
-               plci->NL.ReqCh = 0;
-               plci->NL.Req = plci->nl_req = (byte) N_UDATA;
-               plci->adapter->request(&plci->NL);
-               return;
-       case RTP_CONNECT_B3_RES_COMMAND_3:
-               return;
-       }
-}
-
-
-
-static void hold_save_command(dword Id, PLCI *plci, byte Rc)
-{
-       byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: hold_save_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               if (!plci->NL.Id)
-                       break;
-               plci->command = 0;
-               plci->adjust_b_parms_msg = NULL;
-               plci->adjust_b_facilities = plci->B1_facilities;
-               plci->adjust_b_command = HOLD_SAVE_COMMAND_1;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23;
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: HOLD save...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case HOLD_SAVE_COMMAND_1:
-               Info = adjust_b_process(Id, plci, Rc);
-               if (Info != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: HOLD save failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       break;
-               }
-               if (plci->internal_command)
-                       return;
-       }
-       sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
-}
-
-
-static void retrieve_restore_command(dword Id, PLCI *plci, byte Rc)
-{
-       byte SS_Ind[] = "\x05\x03\x00\x02\x00\x00"; /* Retrieve_Ind struct*/
-       word Info;
-       word internal_command;
-
-       dbug(1, dprintf("[%06lx] %s,%d: retrieve_restore_command %02x %04x",
-                       UnMapId(Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
-
-       Info = GOOD;
-       internal_command = plci->internal_command;
-       plci->internal_command = 0;
-       switch (internal_command)
-       {
-       default:
-               plci->command = 0;
-               plci->adjust_b_parms_msg = NULL;
-               plci->adjust_b_facilities = plci->B1_facilities;
-               plci->adjust_b_command = RETRIEVE_RESTORE_COMMAND_1;
-               plci->adjust_b_ncci = (word)(Id >> 16);
-               plci->adjust_b_mode = ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
-               plci->adjust_b_state = ADJUST_B_START;
-               dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore...",
-                               UnMapId(Id), (char *)(FILE_), __LINE__));
-               /* fall through */
-       case RETRIEVE_RESTORE_COMMAND_1:
-               Info = adjust_b_process(Id, plci, Rc);
-               if (Info != GOOD)
-               {
-                       dbug(1, dprintf("[%06lx] %s,%d: RETRIEVE restore failed",
-                                       UnMapId(Id), (char *)(FILE_), __LINE__));
-                       break;
-               }
-               if (plci->internal_command)
-                       return;
-       }
-       sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
-}
-
-
-static void init_b1_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: init_b1_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       plci->B1_resource = 0;
-       plci->B1_facilities = 0;
-
-       plci->li_bchannel_id = 0;
-       mixer_clear_config(plci);
-
-
-       ec_clear_config(plci);
-
-
-       dtmf_rec_clear_config(plci);
-       dtmf_send_clear_config(plci);
-       dtmf_parameter_clear_config(plci);
-
-       adv_voice_clear_config(plci);
-       adjust_b_clear(plci);
-}
-
-
-static void clear_b1_config(PLCI *plci)
-{
-
-       dbug(1, dprintf("[%06lx] %s,%d: clear_b1_config",
-                       (dword)((plci->Id << 8) | UnMapController(plci->adapter->Id)),
-                       (char *)(FILE_), __LINE__));
-
-       adv_voice_clear_config(plci);
-       adjust_b_clear(plci);
-
-       ec_clear_config(plci);
-
-
-       dtmf_rec_clear_config(plci);
-       dtmf_send_clear_config(plci);
-       dtmf_parameter_clear_config(plci);
-
-
-       if ((plci->li_bchannel_id != 0)
-           && (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci == plci))
-       {
-               mixer_clear_config(plci);
-               li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = NULL;
-               plci->li_bchannel_id = 0;
-       }
-
-       plci->B1_resource = 0;
-       plci->B1_facilities = 0;
-}
-
-
-/* -----------------------------------------------------------------
-   XON protocol local helpers
-   ----------------------------------------------------------------- */
-static void channel_flow_control_remove(PLCI *plci) {
-       DIVA_CAPI_ADAPTER *a = plci->adapter;
-       word i;
-       for (i = 1; i < MAX_NL_CHANNEL + 1; i++) {
-               if (a->ch_flow_plci[i] == plci->Id) {
-                       a->ch_flow_plci[i] = 0;
-                       a->ch_flow_control[i] = 0;
-               }
-       }
-}
-
-static void channel_x_on(PLCI *plci, byte ch) {
-       DIVA_CAPI_ADAPTER *a = plci->adapter;
-       if (a->ch_flow_control[ch] & N_XON_SENT) {
-               a->ch_flow_control[ch] &= ~N_XON_SENT;
-       }
-}
-
-static void channel_x_off(PLCI *plci, byte ch, byte flag) {
-       DIVA_CAPI_ADAPTER *a = plci->adapter;
-       if ((a->ch_flow_control[ch] & N_RX_FLOW_CONTROL_MASK) == 0) {
-               a->ch_flow_control[ch] |= (N_CH_XOFF | flag);
-               a->ch_flow_plci[ch] = plci->Id;
-               a->ch_flow_control_pending++;
-       }
-}
-
-static void channel_request_xon(PLCI *plci, byte ch) {
-       DIVA_CAPI_ADAPTER *a = plci->adapter;
-
-       if (a->ch_flow_control[ch] & N_CH_XOFF) {
-               a->ch_flow_control[ch] |= N_XON_REQ;
-               a->ch_flow_control[ch] &= ~N_CH_XOFF;
-               a->ch_flow_control[ch] &= ~N_XON_CONNECT_IND;
-       }
-}
-
-static void channel_xmit_extended_xon(PLCI *plci) {
-       DIVA_CAPI_ADAPTER *a;
-       int max_ch = ARRAY_SIZE(a->ch_flow_control);
-       int i, one_requested = 0;
-
-       if ((!plci) || (!plci->Id) || ((a = plci->adapter) == NULL)) {
-               return;
-       }
-
-       for (i = 0; i < max_ch; i++) {
-               if ((a->ch_flow_control[i] & N_CH_XOFF) &&
-                   (a->ch_flow_control[i] & N_XON_CONNECT_IND) &&
-                   (plci->Id == a->ch_flow_plci[i])) {
-                       channel_request_xon(plci, (byte)i);
-                       one_requested = 1;
-               }
-       }
-
-       if (one_requested) {
-               channel_xmit_xon(plci);
-       }
-}
-
-/*
-  Try to xmit next X_ON
-*/
-static int find_channel_with_pending_x_on(DIVA_CAPI_ADAPTER *a, PLCI *plci) {
-       int max_ch = ARRAY_SIZE(a->ch_flow_control);
-       int i;
-
-       if (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)) {
-               return (0);
-       }
-
-       if (a->last_flow_control_ch >= max_ch) {
-               a->last_flow_control_ch = 1;
-       }
-       for (i = a->last_flow_control_ch; i < max_ch; i++) {
-               if ((a->ch_flow_control[i] & N_XON_REQ) &&
-                   (plci->Id == a->ch_flow_plci[i])) {
-                       a->last_flow_control_ch = i + 1;
-                       return (i);
-               }
-       }
-
-       for (i = 1; i < a->last_flow_control_ch; i++) {
-               if ((a->ch_flow_control[i] & N_XON_REQ) &&
-                   (plci->Id == a->ch_flow_plci[i])) {
-                       a->last_flow_control_ch = i + 1;
-                       return (i);
-               }
-       }
-
-       return (0);
-}
-
-static void channel_xmit_xon(PLCI *plci) {
-       DIVA_CAPI_ADAPTER *a = plci->adapter;
-       byte ch;
-
-       if (plci->nl_req || !plci->NL.Id || plci->nl_remove_id) {
-               return;
-       }
-       if ((ch = (byte)find_channel_with_pending_x_on(a, plci)) == 0) {
-               return;
-       }
-       a->ch_flow_control[ch] &= ~N_XON_REQ;
-       a->ch_flow_control[ch] |= N_XON_SENT;
-
-       plci->NL.Req = plci->nl_req = (byte)N_XON;
-       plci->NL.ReqCh         = ch;
-       plci->NL.X             = plci->NData;
-       plci->NL.XNum          = 1;
-       plci->NData[0].P       = &plci->RBuffer[0];
-       plci->NData[0].PLength = 0;
-
-       plci->adapter->request(&plci->NL);
-}
-
-static int channel_can_xon(PLCI *plci, byte ch) {
-       APPL *APPLptr;
-       DIVA_CAPI_ADAPTER *a;
-       word NCCIcode;
-       dword count;
-       word Num;
-       word i;
-
-       APPLptr = plci->appl;
-       a = plci->adapter;
-
-       if (!APPLptr)
-               return (0);
-
-       NCCIcode = a->ch_ncci[ch] | (((word) a->Id) << 8);
-
-       /* count all buffers within the Application pool    */
-       /* belonging to the same NCCI. XON if a first is    */
-       /* used.                                            */
-       count = 0;
-       Num = 0xffff;
-       for (i = 0; i < APPLptr->MaxBuffer; i++) {
-               if (NCCIcode == APPLptr->DataNCCI[i]) count++;
-               if (!APPLptr->DataNCCI[i] && Num == 0xffff) Num = i;
-       }
-       if ((count > 2) || (Num == 0xffff)) {
-               return (0);
-       }
-       return (1);
-}
-
-
-/*------------------------------------------------------------------*/
-
-static word CPN_filter_ok(byte *cpn, DIVA_CAPI_ADAPTER *a, word offset)
-{
-       return 1;
-}
-
-
-
-/**********************************************************************************/
-/* function groups the listening applications according to the CIP mask and the   */
-/* Info_Mask. Each group gets just one Connect_Ind. Some application manufacturer */
-/* are not multi-instance capable, so they start e.g. 30 applications what causes */
-/* big problems on application level (one call, 30 Connect_Ind, ect). The         */
-/* function must be enabled by setting "a->group_optimization_enabled" from the   */
-/* OS specific part (per adapter).                                                */
-/**********************************************************************************/
-static void group_optimization(DIVA_CAPI_ADAPTER *a, PLCI *plci)
-{
-       word i, j, k, busy, group_found;
-       dword info_mask_group[MAX_CIP_TYPES];
-       dword cip_mask_group[MAX_CIP_TYPES];
-       word appl_number_group_type[MAX_APPL];
-       PLCI *auxplci;
-
-       /* all APPLs within this inc. call are allowed to dial in */
-       bitmap_fill(plci->group_optimization_mask_table, MAX_APPL);
-
-       if (!a->group_optimization_enabled)
-       {
-               dbug(1, dprintf("No group optimization"));
-               return;
-       }
-
-       dbug(1, dprintf("Group optimization = 0x%x...", a->group_optimization_enabled));
-
-       for (i = 0; i < MAX_CIP_TYPES; i++)
-       {
-               info_mask_group[i] = 0;
-               cip_mask_group[i] = 0;
-       }
-       for (i = 0; i < MAX_APPL; i++)
-       {
-               appl_number_group_type[i] = 0;
-       }
-       for (i = 0; i < max_appl; i++) /* check if any multi instance capable application is present */
-       {  /* group_optimization set to 1 means not to optimize multi-instance capable applications (default) */
-               if (application[i].Id && (application[i].MaxNCCI) > 1 && (a->CIP_Mask[i]) && (a->group_optimization_enabled == 1))
-               {
-                       dbug(1, dprintf("Multi-Instance capable, no optimization required"));
-                       return; /* allow good application unfiltered access */
-               }
-       }
-       for (i = 0; i < max_appl; i++) /* Build CIP Groups */
-       {
-               if (application[i].Id && a->CIP_Mask[i])
-               {
-                       for (k = 0, busy = false; k < a->max_plci; k++)
-                       {
-                               if (a->plci[k].Id)
-                               {
-                                       auxplci = &a->plci[k];
-                                       if (auxplci->appl == &application[i]) {
-                                               /* application has a busy PLCI */
-                                               busy = true;
-                                               dbug(1, dprintf("Appl 0x%x is busy", i + 1));
-                                       } else if (test_bit(i, plci->c_ind_mask_table)) {
-                                               /* application has an incoming call pending */
-                                               busy = true;
-                                               dbug(1, dprintf("Appl 0x%x has inc. call pending", i + 1));
-                                       }
-                               }
-                       }
-
-                       for (j = 0, group_found = 0; j <= (MAX_CIP_TYPES) && !busy && !group_found; j++)     /* build groups with free applications only */
-                       {
-                               if (j == MAX_CIP_TYPES)       /* all groups are in use but group still not found */
-                               {                           /* the MAX_CIP_TYPES group enables all calls because of field overflow */
-                                       appl_number_group_type[i] = MAX_CIP_TYPES;
-                                       group_found = true;
-                                       dbug(1, dprintf("Field overflow appl 0x%x", i + 1));
-                               }
-                               else if ((info_mask_group[j] == a->CIP_Mask[i]) && (cip_mask_group[j] == a->Info_Mask[i]))
-                               {                                      /* is group already present ?                  */
-                                       appl_number_group_type[i] = j | 0x80;  /* store the group number for each application */
-                                       group_found = true;
-                                       dbug(1, dprintf("Group 0x%x found with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j]));
-                               }
-                               else if (!info_mask_group[j])
-                               {                                      /* establish a new group                       */
-                                       appl_number_group_type[i] = j | 0x80;  /* store the group number for each application */
-                                       info_mask_group[j] = a->CIP_Mask[i]; /* store the new CIP mask for the new group    */
-                                       cip_mask_group[j] = a->Info_Mask[i]; /* store the new Info_Mask for this new group  */
-                                       group_found = true;
-                                       dbug(1, dprintf("New Group 0x%x established with appl 0x%x, CIP=0x%lx", appl_number_group_type[i], i + 1, info_mask_group[j]));
-                               }
-                       }
-               }
-       }
-
-       for (i = 0; i < max_appl; i++) /* Build group_optimization_mask_table */
-       {
-               if (appl_number_group_type[i]) /* application is free, has listens and is member of a group */
-               {
-                       if (appl_number_group_type[i] == MAX_CIP_TYPES)
-                       {
-                               dbug(1, dprintf("OverflowGroup 0x%x, valid appl = 0x%x, call enabled", appl_number_group_type[i], i + 1));
-                       }
-                       else
-                       {
-                               dbug(1, dprintf("Group 0x%x, valid appl = 0x%x", appl_number_group_type[i], i + 1));
-                               for (j = i + 1; j < max_appl; j++)   /* search other group members and mark them as busy        */
-                               {
-                                       if (appl_number_group_type[i] == appl_number_group_type[j])
-                                       {
-                                               dbug(1, dprintf("Appl 0x%x is member of group 0x%x, no call", j + 1, appl_number_group_type[j]));
-                                               /* disable call on other group members */
-                                               __clear_bit(j, plci->group_optimization_mask_table);
-                                               appl_number_group_type[j] = 0;       /* remove disabled group member from group list */
-                                       }
-                               }
-                       }
-               }
-               else                                                 /* application should not get a call */
-               {
-                       __clear_bit(i, plci->group_optimization_mask_table);
-               }
-       }
-
-}
-
-
-
-/* OS notifies the driver about a application Capi_Register */
-word CapiRegister(word id)
-{
-       word i, j, appls_found;
-
-       PLCI *plci;
-       DIVA_CAPI_ADAPTER *a;
-
-       for (i = 0, appls_found = 0; i < max_appl; i++)
-       {
-               if (application[i].Id && (application[i].Id != id))
-               {
-                       appls_found++;                       /* an application has been found */
-               }
-       }
-
-       if (appls_found) return true;
-       for (i = 0; i < max_adapter; i++)                   /* scan all adapters...    */
-       {
-               a = &adapter[i];
-               if (a->request)
-               {
-                       if (a->flag_dynamic_l1_down)  /* remove adapter from L1 tristate (Huntgroup) */
-                       {
-                               if (!appls_found)           /* first application does a capi register   */
-                               {
-                                       if ((j = get_plci(a)))                    /* activate L1 of all adapters */
-                                       {
-                                               plci = &a->plci[j - 1];
-                                               plci->command = 0;
-                                               add_p(plci, OAD, "\x01\xfd");
-                                               add_p(plci, CAI, "\x01\x80");
-                                               add_p(plci, UID, "\x06\x43\x61\x70\x69\x32\x30");
-                                               add_p(plci, SHIFT | 6, NULL);
-                                               add_p(plci, SIN, "\x02\x00\x00");
-                                               plci->internal_command = START_L1_SIG_ASSIGN_PEND;
-                                               sig_req(plci, ASSIGN, DSIG_ID);
-                                               add_p(plci, FTY, "\x02\xff\x07"); /* l1 start */
-                                               sig_req(plci, SIG_CTRL, 0);
-                                               send_req(plci);
-                                       }
-                               }
-                       }
-               }
-       }
-       return false;
-}
-
-/*------------------------------------------------------------------*/
-
-/* Functions for virtual Switching e.g. Transfer by join, Conference */
-
-static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms)
-{
-       word i;
-       /* Format of vswitch_t:
-          0 byte length
-          1 byte VSWITCHIE
-          2 byte VSWITCH_REQ/VSWITCH_IND
-          3 byte reserved
-          4 word VSwitchcommand
-          6 word returnerror
-          8... Params
-       */
-       if (!plci ||
-           !plci->appl ||
-           !plci->State ||
-           plci->Sig.Ind == NCR_FACILITY
-               )
-               return;
-
-       for (i = 0; i < MAX_MULTI_IE; i++)
-       {
-               if (!parms[i][0]) continue;
-               if (parms[i][0] < 7)
-               {
-                       parms[i][0] = 0; /* kill it */
-                       continue;
-               }
-               dbug(1, dprintf("VSwitchReqInd(%d)", parms[i][4]));
-               switch (parms[i][4])
-               {
-               case VSJOIN:
-                       if (!plci->relatedPTYPLCI ||
-                           (plci->ptyState != S_ECT && plci->relatedPTYPLCI->ptyState != S_ECT))
-                       { /* Error */
-                               break;
-                       }
-                       /* remember all necessary informations */
-                       if (parms[i][0] != 11 || parms[i][8] != 3) /* Length Test */
-                       {
-                               break;
-                       }
-                       if (parms[i][2] == VSWITCH_IND && parms[i][9] == 1)
-                       {   /* first indication after ECT-Request on Consultation Call */
-                               plci->vswitchstate = parms[i][9];
-                               parms[i][9] = 2; /* State */
-                               /* now ask first Call to join */
-                       }
-                       else if (parms[i][2] == VSWITCH_REQ && parms[i][9] == 3)
-                       { /* Answer of VSWITCH_REQ from first Call */
-                               plci->vswitchstate = parms[i][9];
-                               /* tell consultation call to join
-                                  and the protocol capabilities of the first call */
-                       }
-                       else
-                       { /* Error */
-                               break;
-                       }
-                       plci->vsprot = parms[i][10]; /* protocol */
-                       plci->vsprotdialect = parms[i][11]; /* protocoldialect */
-                       /* send join request to related PLCI */
-                       parms[i][1] = VSWITCHIE;
-                       parms[i][2] = VSWITCH_REQ;
-
-                       plci->relatedPTYPLCI->command = 0;
-                       plci->relatedPTYPLCI->internal_command = VSWITCH_REQ_PEND;
-                       add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]);
-                       sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
-                       send_req(plci->relatedPTYPLCI);
-                       break;
-               case VSTRANSPORT:
-               default:
-                       if (plci->relatedPTYPLCI &&
-                           plci->vswitchstate == 3 &&
-                           plci->relatedPTYPLCI->vswitchstate == 3)
-                       {
-                               add_p(plci->relatedPTYPLCI, ESC, &parms[i][0]);
-                               sig_req(plci->relatedPTYPLCI, VSWITCH_REQ, 0);
-                               send_req(plci->relatedPTYPLCI);
-                       }
-                       break;
-               }
-               parms[i][0] = 0; /* kill it */
-       }
-}
-
-
-/*------------------------------------------------------------------*/
-
-static int diva_get_dma_descriptor(PLCI *plci, dword   *dma_magic) {
-       ENTITY e;
-       IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;
-
-       if (!(diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_RX_DMA)) {
-               return (-1);
-       }
-
-       pReq->xdi_dma_descriptor_operation.Req = 0;
-       pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
-
-       pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_number  = -1;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
-
-       e.user[0] = plci->adapter->Id - 1;
-       plci->adapter->request((ENTITY *)pReq);
-
-       if (!pReq->xdi_dma_descriptor_operation.info.operation &&
-           (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
-           pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
-               *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
-               dbug(3, dprintf("dma_alloc, a:%d (%d-%08x)",
-                               plci->adapter->Id,
-                               pReq->xdi_dma_descriptor_operation.info.descriptor_number,
-                               *dma_magic));
-               return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
-       } else {
-               dbug(1, dprintf("dma_alloc failed"));
-               return (-1);
-       }
-}
-
-static void diva_free_dma_descriptor(PLCI *plci, int nr) {
-       ENTITY e;
-       IDI_SYNC_REQ *pReq = (IDI_SYNC_REQ *)&e;
-
-       if (nr < 0) {
-               return;
-       }
-
-       pReq->xdi_dma_descriptor_operation.Req = 0;
-       pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
-
-       pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_number  = nr;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
-       pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
-
-       e.user[0] = plci->adapter->Id - 1;
-       plci->adapter->request((ENTITY *)pReq);
-
-       if (!pReq->xdi_dma_descriptor_operation.info.operation) {
-               dbug(1, dprintf("dma_free(%d)", nr));
-       } else {
-               dbug(1, dprintf("dma_free failed (%d)", nr));
-       }
-}
-
-/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mi_pc.h b/drivers/isdn/hardware/eicon/mi_pc.h
deleted file mode 100644 (file)
index 83e9ed8..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-/*----------------------------------------------------------------------------
-// MAESTRA ISA PnP */
-#define BRI_MEMORY_BASE                 0x1f700000
-#define BRI_MEMORY_SIZE                 0x00100000  /* 1MB on the BRI                         */
-#define BRI_SHARED_RAM_SIZE             0x00010000  /* 64k shared RAM                         */
-#define BRI_RAY_TAYLOR_DSP_CODE_SIZE    0x00020000  /* max 128k DSP-Code (Ray Taylor's code)  */
-#define BRI_ORG_MAX_DSP_CODE_SIZE       0x00050000  /* max 320k DSP-Code (Telindus)           */
-#define BRI_V90D_MAX_DSP_CODE_SIZE      0x00060000  /* max 384k DSP-Code if V.90D included    */
-#define BRI_CACHED_ADDR(x)              (((x) & 0x1fffffffL) | 0x80000000L)
-#define BRI_UNCACHED_ADDR(x)            (((x) & 0x1fffffffL) | 0xa0000000L)
-#define ADDR  4
-#define ADDRH 6
-#define DATA  0
-#define RESET 7
-#define DEFAULT_ADDRESS 0x240
-#define DEFAULT_IRQ     3
-#define M_PCI_ADDR   0x04  /* MAESTRA BRI PCI */
-#define M_PCI_ADDRH  0x0c  /* MAESTRA BRI PCI */
-#define M_PCI_DATA   0x00  /* MAESTRA BRI PCI */
-#define M_PCI_RESET  0x10  /* MAESTRA BRI PCI */
-/*----------------------------------------------------------------------------
-// MAESTRA PRI PCI */
-#define MP_IRQ_RESET                    0xc18       /* offset of isr in the CONFIG memory bar */
-#define MP_IRQ_RESET_VAL                0xfe        /* value to clear an interrupt            */
-#define MP_MEMORY_SIZE                  0x00400000  /* 4MB on standard PRI                    */
-#define MP2_MEMORY_SIZE                 0x00800000  /* 8MB on PRI Rev. 2                      */
-#define MP_SHARED_RAM_OFFSET            0x00001000  /* offset of shared RAM base in the DRAM memory bar */
-#define MP_SHARED_RAM_SIZE              0x00010000  /* 64k shared RAM                         */
-#define MP_PROTOCOL_OFFSET              (MP_SHARED_RAM_OFFSET + MP_SHARED_RAM_SIZE)
-#define MP_RAY_TAYLOR_DSP_CODE_SIZE     0x00040000  /* max 256k DSP-Code (Ray Taylor's code)  */
-#define MP_ORG_MAX_DSP_CODE_SIZE        0x00060000  /* max 384k DSP-Code (Telindus)           */
-#define MP_V90D_MAX_DSP_CODE_SIZE       0x00070000  /* max 448k DSP-Code if V.90D included)   */
-#define MP_VOIP_MAX_DSP_CODE_SIZE       0x00090000  /* max 576k DSP-Code if voice over IP included */
-#define MP_CACHED_ADDR(x)               (((x) & 0x1fffffffL) | 0x80000000L)
-#define MP_UNCACHED_ADDR(x)             (((x) & 0x1fffffffL) | 0xa0000000L)
-#define MP_RESET         0x20        /* offset of RESET register in the DEVICES memory bar */
-/* RESET register bits */
-#define _MP_S2M_RESET    0x10        /* active lo   */
-#define _MP_LED2         0x08        /* 1 = on      */
-#define _MP_LED1         0x04        /* 1 = on      */
-#define _MP_DSP_RESET    0x02        /* active lo   */
-#define _MP_RISC_RESET   0x81        /* active hi, bit 7 for compatibility with old boards */
-/* CPU exception context structure in MP shared ram after trap */
-typedef struct mp_xcptcontext_s MP_XCPTC;
-struct mp_xcptcontext_s {
-       dword       sr;
-       dword       cr;
-       dword       epc;
-       dword       vaddr;
-       dword       regs[32];
-       dword       mdlo;
-       dword       mdhi;
-       dword       reseverd;
-       dword       xclass;
-};
-/* boot interface structure for PRI */
-struct mp_load {
-       dword     volatile cmd;
-       dword     volatile addr;
-       dword     volatile len;
-       dword     volatile err;
-       dword     volatile live;
-       dword     volatile res1[0x1b];
-       dword     volatile TrapId;    /* has value 0x999999XX on a CPU trap */
-       dword     volatile res2[0x03];
-       MP_XCPTC  volatile xcpt;      /* contains register dump */
-       dword     volatile rest[((0x1020 >> 2) - 6) - 0x1b - 1 - 0x03 - (sizeof(MP_XCPTC) >> 2)];
-       dword     volatile signature;
-       dword data[60000]; /* real interface description */
-};
-/*----------------------------------------------------------------------------*/
-/* SERVER 4BRI (Quattro PCI)                                                  */
-#define MQ_BOARD_REG_OFFSET             0x800000    /* PC relative On board registers offset  */
-#define MQ_BREG_RISC                    0x1200      /* RISC Reset ect                         */
-#define MQ_RISC_COLD_RESET_MASK         0x0001      /* RISC Cold reset                        */
-#define MQ_RISC_WARM_RESET_MASK         0x0002      /* RISC Warm reset                        */
-#define MQ_BREG_IRQ_TEST                0x0608      /* Interrupt request, no CPU interaction  */
-#define MQ_IRQ_REQ_ON                   0x1
-#define MQ_IRQ_REQ_OFF                  0x0
-#define MQ_BOARD_DSP_OFFSET             0xa00000    /* PC relative On board DSP regs offset   */
-#define MQ_DSP1_ADDR_OFFSET             0x0008      /* Addr register offset DSP 1 subboard 1  */
-#define MQ_DSP2_ADDR_OFFSET             0x0208      /* Addr register offset DSP 2 subboard 1  */
-#define MQ_DSP1_DATA_OFFSET             0x0000      /* Data register offset DSP 1 subboard 1  */
-#define MQ_DSP2_DATA_OFFSET             0x0200      /* Data register offset DSP 2 subboard 1  */
-#define MQ_DSP_JUNK_OFFSET              0x0400      /* DSP Data/Addr regs subboard offset     */
-#define MQ_ISAC_DSP_RESET               0x0028      /* ISAC and DSP reset address offset      */
-#define MQ_BOARD_ISAC_DSP_RESET         0x800028    /* ISAC and DSP reset address offset      */
-#define MQ_INSTANCE_COUNT               4           /* 4BRI consists of four instances        */
-#define MQ_MEMORY_SIZE                  0x00400000  /* 4MB on standard 4BRI                   */
-#define MQ_CTRL_SIZE                    0x00002000  /* 8K memory mapped registers             */
-#define MQ_SHARED_RAM_SIZE              0x00010000  /* 64k shared RAM                         */
-#define MQ_ORG_MAX_DSP_CODE_SIZE        0x00050000  /* max 320k DSP-Code (Telindus) */
-#define MQ_V90D_MAX_DSP_CODE_SIZE       0x00060000  /* max 384K DSP-Code if V.90D included */
-#define MQ_VOIP_MAX_DSP_CODE_SIZE       0x00028000  /* max 4*160k = 640K DSP-Code if voice over IP included */
-#define MQ_CACHED_ADDR(x)               (((x) & 0x1fffffffL) | 0x80000000L)
-#define MQ_UNCACHED_ADDR(x)             (((x) & 0x1fffffffL) | 0xa0000000L)
-/*--------------------------------------------------------------------------------------------*/
-/* Additional definitions reflecting the different address map of the  SERVER 4BRI V2          */
-#define MQ2_BREG_RISC                   0x0200      /* RISC Reset ect                         */
-#define MQ2_BREG_IRQ_TEST               0x0400      /* Interrupt request, no CPU interaction  */
-#define MQ2_BOARD_DSP_OFFSET            0x800000    /* PC relative On board DSP regs offset   */
-#define MQ2_DSP1_DATA_OFFSET            0x1800      /* Data register offset DSP 1 subboard 1  */
-#define MQ2_DSP1_ADDR_OFFSET            0x1808      /* Addr register offset DSP 1 subboard 1  */
-#define MQ2_DSP2_DATA_OFFSET            0x1810      /* Data register offset DSP 2 subboard 1  */
-#define MQ2_DSP2_ADDR_OFFSET            0x1818      /* Addr register offset DSP 2 subboard 1  */
-#define MQ2_DSP_JUNK_OFFSET             0x1000      /* DSP Data/Addr regs subboard offset     */
-#define MQ2_ISAC_DSP_RESET              0x0000      /* ISAC and DSP reset address offset      */
-#define MQ2_BOARD_ISAC_DSP_RESET        0x800000    /* ISAC and DSP reset address offset      */
-#define MQ2_IPACX_CONFIG                0x0300      /* IPACX Configuration TE(0)/NT(1)        */
-#define MQ2_BOARD_IPACX_CONFIG          0x800300    /*     ""                                 */
-#define MQ2_MEMORY_SIZE                 0x01000000  /* 16MB code/data memory                  */
-#define MQ2_CTRL_SIZE                   0x00008000  /* 32K memory mapped registers            */
-/*----------------------------------------------------------------------------*/
-/* SERVER BRI 2M/2F as derived from 4BRI V2                                   */
-#define BRI2_MEMORY_SIZE                0x00800000  /* 8MB code/data memory                   */
-#define BRI2_PROTOCOL_MEMORY_SIZE       (MQ2_MEMORY_SIZE >> 2) /*  same as one 4BRI Rev.2 task */
-#define BRI2_CTRL_SIZE                  0x00008000  /* 32K memory mapped registers            */
-#define M_INSTANCE_COUNT                1           /*  BRI consists of one instance          */
-/*
- * Some useful constants for proper initialization of the GT6401x
- */
-#define ID_REG        0x0000      /*Pci reg-contain the Dev&Ven ID of the card*/
-#define RAS0_BASEREG  0x0010      /*Ras0 register - contain the base addr Ras0*/
-#define RAS2_BASEREG  0x0014
-#define CS_BASEREG    0x0018
-#define BOOT_BASEREG  0x001c
-#define GTREGS_BASEREG 0x0024   /*GTRegsBase reg-contain the base addr where*/
-                               /*the GT64010 internal regs where mapped    */
-/*
- *  GT64010 internal registers
- */
-/* DRAM device coding  */
-#define LOW_RAS0_DREG 0x0400    /*Ras0 low decode address*/
-#define HI_RAS0_DREG  0x0404    /*Ras0 high decode address*/
-#define LOW_RAS1_DREG 0x0408    /*Ras1 low decode address*/
-#define HI_RAS1_DREG  0x040c    /*Ras1 high decode address*/
-#define LOW_RAS2_DREG 0x0410    /*Ras2 low decode address*/
-#define HI_RAS2_DREG  0x0414    /*Ras2 high decode address*/
-#define LOW_RAS3_DREG 0x0418    /*Ras3 low decode address*/
-#define HI_RAS3_DREG  0x041c    /*Ras3 high decode address*/
-/* I/O CS device coding  */
-#define LOW_CS0_DREG  0x0420 /* CS0* low decode register */
-#define HI_CS0_DREG   0x0424 /* CS0* high decode register */
-#define LOW_CS1_DREG  0x0428 /* CS1* low decode register */
-#define HI_CS1_DREG   0x042c /* CS1* high decode register */
-#define LOW_CS2_DREG  0x0430 /* CS2* low decode register */
-#define HI_CS2_DREG   0x0434 /* CS2* high decode register */
-#define LOW_CS3_DREG  0x0438 /* CS3* low decode register */
-#define HI_CS3_DREG   0x043c /* CS3* high decode register */
-/* Boot PROM device coding */
-#define LOW_BOOTCS_DREG 0x0440 /* Boot CS low decode register */
-#define HI_BOOTCS_DREG 0x0444 /* Boot CS High decode register */
-/* DRAM group coding (for CPU)  */
-#define LO_RAS10_GREG 0x0008    /*Ras1..0 group low decode address*/
-#define HI_RAS10_GREG 0x0010    /*Ras1..0 group high decode address*/
-#define LO_RAS32_GREG 0x0018    /*Ras3..2 group low decode address  */
-#define HI_RAS32_GREG 0x0020    /*Ras3..2 group high decode address  */
-/* I/O CS group coding for (CPU)  */
-#define LO_CS20_GREG  0x0028 /* CS2..0 group low decode register */
-#define HI_CS20_GREG  0x0030 /* CS2..0 group high decode register */
-#define LO_CS3B_GREG  0x0038 /* CS3 & PROM group low decode register */
-#define HI_CS3B_GREG  0x0040 /* CS3 & PROM group high decode register */
-/* Galileo specific PCI config. */
-#define PCI_TIMEOUT_RET 0x0c04 /* Time Out and retry register */
-#define RAS10_BANKSIZE 0x0c08 /* RAS 1..0 group PCI bank size */
-#define RAS32_BANKSIZE 0x0c0c /* RAS 3..2 group PCI bank size */
-#define CS20_BANKSIZE 0x0c10 /* CS 2..0 group PCI bank size */
-#define CS3B_BANKSIZE 0x0c14 /* CS 3 & Boot group PCI bank size */
-#define DRAM_SIZE     0x0001      /*Dram size in mega bytes*/
-#define PROM_SIZE     0x08000     /*Prom size in bytes*/
-/*--------------------------------------------------------------------------*/
-#define OFFS_DIVA_INIT_TASK_COUNT 0x68
-#define OFFS_DSP_CODE_BASE_ADDR   0x6c
-#define OFFS_XLOG_BUF_ADDR        0x70
-#define OFFS_XLOG_COUNT_ADDR      0x74
-#define OFFS_XLOG_OUT_ADDR        0x78
-#define OFFS_PROTOCOL_END_ADDR    0x7c
-#define OFFS_PROTOCOL_ID_STRING   0x80
-/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mntfunc.c b/drivers/isdn/hardware/eicon/mntfunc.c
deleted file mode 100644 (file)
index 1cd9aff..0000000
+++ /dev/null
@@ -1,370 +0,0 @@
-/* $Id: mntfunc.c,v 1.19.6.4 2005/01/31 12:22:20 armin Exp $
- *
- * Driver for Eicon DIVA Server ISDN cards.
- * Maint module
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-
-#include "platform.h"
-#include "di_defs.h"
-#include "divasync.h"
-#include "debug_if.h"
-
-extern char *DRIVERRELEASE_MNT;
-
-#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
-#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
-
-extern void DIVA_DIDD_Read(void *, int);
-
-static dword notify_handle;
-static DESCRIPTOR DAdapter;
-static DESCRIPTOR MAdapter;
-static DESCRIPTOR MaintDescriptor =
-{ IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp };
-
-extern int diva_os_copy_to_user(void *os_handle, void __user *dst,
-                               const void *src, int length);
-extern int diva_os_copy_from_user(void *os_handle, void *dst,
-                                 const void __user *src, int length);
-
-static void no_printf(unsigned char *x, ...)
-{
-       /* dummy debug function */
-}
-
-#include "debuglib.c"
-
-/*
- *  DIDD callback function
- */
-static void *didd_callback(void *context, DESCRIPTOR *adapter,
-                          int removal)
-{
-       if (adapter->type == IDI_DADAPTER) {
-               DBG_ERR(("cb: Change in DAdapter ? Oops ?."));
-       } else if (adapter->type == IDI_DIMAINT) {
-               if (removal) {
-                       DbgDeregister();
-                       memset(&MAdapter, 0, sizeof(MAdapter));
-                       dprintf = no_printf;
-               } else {
-                       memcpy(&MAdapter, adapter, sizeof(MAdapter));
-                       dprintf = (DIVA_DI_PRINTF) MAdapter.request;
-                       DbgRegister("MAINT", DRIVERRELEASE_MNT, DBG_DEFAULT);
-               }
-       } else if ((adapter->type > 0) && (adapter->type < 16)) {
-               if (removal) {
-                       diva_mnt_remove_xdi_adapter(adapter);
-               } else {
-                       diva_mnt_add_xdi_adapter(adapter);
-               }
-       }
-       return (NULL);
-}
-
-/*
- * connect to didd
- */
-static int __init connect_didd(void)
-{
-       int x = 0;
-       int dadapter = 0;
-       IDI_SYNC_REQ req;
-       DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
-
-       DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
-
-       for (x = 0; x < MAX_DESCRIPTORS; x++) {
-               if (DIDD_Table[x].type == IDI_DADAPTER) {       /* DADAPTER found */
-                       dadapter = 1;
-                       memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
-                       req.didd_notify.e.Req = 0;
-                       req.didd_notify.e.Rc =
-                               IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
-                       req.didd_notify.info.callback = (void *)didd_callback;
-                       req.didd_notify.info.context = NULL;
-                       DAdapter.request((ENTITY *)&req);
-                       if (req.didd_notify.e.Rc != 0xff)
-                               return (0);
-                       notify_handle = req.didd_notify.info.handle;
-                       /* Register MAINT (me) */
-                       req.didd_add_adapter.e.Req = 0;
-                       req.didd_add_adapter.e.Rc =
-                               IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
-                       req.didd_add_adapter.info.descriptor =
-                               (void *) &MaintDescriptor;
-                       DAdapter.request((ENTITY *)&req);
-                       if (req.didd_add_adapter.e.Rc != 0xff)
-                               return (0);
-               } else if ((DIDD_Table[x].type > 0)
-                          && (DIDD_Table[x].type < 16)) {
-                       diva_mnt_add_xdi_adapter(&DIDD_Table[x]);
-               }
-       }
-       return (dadapter);
-}
-
-/*
- * disconnect from didd
- */
-static void __exit disconnect_didd(void)
-{
-       IDI_SYNC_REQ req;
-
-       req.didd_notify.e.Req = 0;
-       req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
-       req.didd_notify.info.handle = notify_handle;
-       DAdapter.request((ENTITY *)&req);
-
-       req.didd_remove_adapter.e.Req = 0;
-       req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
-       req.didd_remove_adapter.info.p_request =
-               (IDI_CALL) MaintDescriptor.request;
-       DAdapter.request((ENTITY *)&req);
-}
-
-/*
- * read/write maint
- */
-int maint_read_write(void __user *buf, int count)
-{
-       byte data[128];
-       dword cmd, id, mask;
-       int ret = 0;
-
-       if (count < (3 * sizeof(dword)))
-               return (-EFAULT);
-
-       if (diva_os_copy_from_user(NULL, (void *) &data[0],
-                                  buf, 3 * sizeof(dword))) {
-               return (-EFAULT);
-       }
-
-       cmd = *(dword *)&data[0];       /* command */
-       id = *(dword *)&data[4];        /* driver id */
-       mask = *(dword *)&data[8];      /* mask or size */
-
-       switch (cmd) {
-       case DITRACE_CMD_GET_DRIVER_INFO:
-               if ((ret = diva_get_driver_info(id, data, sizeof(data))) > 0) {
-                       if ((count < ret) || diva_os_copy_to_user
-                           (NULL, buf, (void *) &data[0], ret))
-                               ret = -EFAULT;
-               } else {
-                       ret = -EINVAL;
-               }
-               break;
-
-       case DITRACE_READ_DRIVER_DBG_MASK:
-               if ((ret = diva_get_driver_dbg_mask(id, (byte *) data)) > 0) {
-                       if ((count < ret) || diva_os_copy_to_user
-                           (NULL, buf, (void *) &data[0], ret))
-                               ret = -EFAULT;
-               } else {
-                       ret = -ENODEV;
-               }
-               break;
-
-       case DITRACE_WRITE_DRIVER_DBG_MASK:
-               if ((ret = diva_set_driver_dbg_mask(id, mask)) <= 0) {
-                       ret = -ENODEV;
-               }
-               break;
-
-               /*
-                 Filter commands will ignore the ID due to fact that filtering affects
-                 the B- channel and Audio Tap trace levels only. Also MAINT driver will
-                 select the right trace ID by itself
-               */
-       case DITRACE_WRITE_SELECTIVE_TRACE_FILTER:
-               if (!mask) {
-                       ret = diva_set_trace_filter(1, "*");
-               } else if (mask < sizeof(data)) {
-                       if (diva_os_copy_from_user(NULL, data, (char __user *)buf + 12, mask)) {
-                               ret = -EFAULT;
-                       } else {
-                               ret = diva_set_trace_filter((int)mask, data);
-                       }
-               } else {
-                       ret = -EINVAL;
-               }
-               break;
-
-       case DITRACE_READ_SELECTIVE_TRACE_FILTER:
-               if ((ret = diva_get_trace_filter(sizeof(data), data)) > 0) {
-                       if (diva_os_copy_to_user(NULL, buf, data, ret))
-                               ret = -EFAULT;
-               } else {
-                       ret = -ENODEV;
-               }
-               break;
-
-       case DITRACE_READ_TRACE_ENTRY:{
-               diva_os_spin_lock_magic_t old_irql;
-               word size;
-               diva_dbg_entry_head_t *pmsg;
-               byte *pbuf;
-
-               if (!(pbuf = diva_os_malloc(0, mask))) {
-                       return (-ENOMEM);
-               }
-
-               for (;;) {
-                       if (!(pmsg =
-                             diva_maint_get_message(&size, &old_irql))) {
-                               break;
-                       }
-                       if (size > mask) {
-                               diva_maint_ack_message(0, &old_irql);
-                               ret = -EINVAL;
-                               break;
-                       }
-                       ret = size;
-                       memcpy(pbuf, pmsg, size);
-                       diva_maint_ack_message(1, &old_irql);
-                       if ((count < size) ||
-                           diva_os_copy_to_user(NULL, buf, (void *) pbuf, size))
-                               ret = -EFAULT;
-                       break;
-               }
-               diva_os_free(0, pbuf);
-       }
-               break;
-
-       case DITRACE_READ_TRACE_ENTRYS:{
-               diva_os_spin_lock_magic_t old_irql;
-               word size;
-               diva_dbg_entry_head_t *pmsg;
-               byte *pbuf = NULL;
-               int written = 0;
-
-               if (mask < 4096) {
-                       ret = -EINVAL;
-                       break;
-               }
-               if (!(pbuf = diva_os_malloc(0, mask))) {
-                       return (-ENOMEM);
-               }
-
-               for (;;) {
-                       if (!(pmsg =
-                             diva_maint_get_message(&size, &old_irql))) {
-                               break;
-                       }
-                       if ((size + 8) > mask) {
-                               diva_maint_ack_message(0, &old_irql);
-                               break;
-                       }
-                       /*
-                         Write entry length
-                       */
-                       pbuf[written++] = (byte) size;
-                       pbuf[written++] = (byte) (size >> 8);
-                       pbuf[written++] = 0;
-                       pbuf[written++] = 0;
-                       /*
-                         Write message
-                       */
-                       memcpy(&pbuf[written], pmsg, size);
-                       diva_maint_ack_message(1, &old_irql);
-                       written += size;
-                       mask -= (size + 4);
-               }
-               pbuf[written++] = 0;
-               pbuf[written++] = 0;
-               pbuf[written++] = 0;
-               pbuf[written++] = 0;
-
-               if ((count < written) || diva_os_copy_to_user(NULL, buf, (void *) pbuf, written)) {
-                       ret = -EFAULT;
-               } else {
-                       ret = written;
-               }
-               diva_os_free(0, pbuf);
-       }
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-       return (ret);
-}
-
-/*
- *  init
- */
-int __init mntfunc_init(int *buffer_length, void **buffer,
-                                   unsigned long diva_dbg_mem)
-{
-       if (*buffer_length < 64) {
-               *buffer_length = 64;
-       }
-       if (*buffer_length > 512) {
-               *buffer_length = 512;
-       }
-       *buffer_length *= 1024;
-
-       if (diva_dbg_mem) {
-               *buffer = (void *) diva_dbg_mem;
-       } else {
-               while ((*buffer_length >= (64 * 1024))
-                      &&
-                      (!(*buffer = diva_os_malloc(0, *buffer_length)))) {
-                       *buffer_length -= 1024;
-               }
-
-               if (!*buffer) {
-                       DBG_ERR(("init: Can not alloc trace buffer"));
-                       return (0);
-               }
-       }
-
-       if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) {
-               if (!diva_dbg_mem) {
-                       diva_os_free(0, *buffer);
-               }
-               DBG_ERR(("init: maint init failed"));
-               return (0);
-       }
-
-       if (!connect_didd()) {
-               DBG_ERR(("init: failed to connect to DIDD."));
-               diva_maint_finit();
-               if (!diva_dbg_mem) {
-                       diva_os_free(0, *buffer);
-               }
-               return (0);
-       }
-       return (1);
-}
-
-/*
- *  exit
- */
-void __exit mntfunc_finit(void)
-{
-       void *buffer;
-       int i = 100;
-
-       DbgDeregister();
-
-       while (diva_mnt_shutdown_xdi_adapters() && i--) {
-               diva_os_sleep(10);
-       }
-
-       disconnect_didd();
-
-       if ((buffer = diva_maint_finit())) {
-               diva_os_free(0, buffer);
-       }
-
-       memset(&MAdapter, 0, sizeof(MAdapter));
-       dprintf = no_printf;
-}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c
deleted file mode 100644 (file)
index 87db5f4..0000000
+++ /dev/null
@@ -1,1132 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* $Id: os_4bri.c,v 1.28.4.4 2005/02/11 19:40:25 armin Exp $ */
-
-#include "platform.h"
-#include "debuglib.h"
-#include "cardtype.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di_defs.h"
-#include "dsp_defs.h"
-#include "di.h"
-#include "io.h"
-
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "os_4bri.h"
-#include "diva_pci.h"
-#include "mi_pc.h"
-#include "dsrv4bri.h"
-#include "helpers.h"
-
-static void *diva_xdiLoadFileFile = NULL;
-static dword diva_xdiLoadFileLength = 0;
-
-/*
-**  IMPORTS
-*/
-extern void prepare_qBri_functions(PISDN_ADAPTER IoAdapter);
-extern void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter);
-extern void diva_xdi_display_adapter_features(int card);
-extern void diva_add_slave_adapter(diva_os_xdi_adapter_t *a);
-
-extern int qBri_FPGA_download(PISDN_ADAPTER IoAdapter);
-extern void start_qBri_hardware(PISDN_ADAPTER IoAdapter);
-
-extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a);
-
-/*
-**  LOCALS
-*/
-static unsigned long _4bri_bar_length[4] = {
-       0x100,
-       0x100,                  /* I/O */
-       MQ_MEMORY_SIZE,
-       0x2000
-};
-static unsigned long _4bri_v2_bar_length[4] = {
-       0x100,
-       0x100,                  /* I/O */
-       MQ2_MEMORY_SIZE,
-       0x10000
-};
-static unsigned long _4bri_v2_bri_bar_length[4] = {
-       0x100,
-       0x100,                  /* I/O */
-       BRI2_MEMORY_SIZE,
-       0x10000
-};
-
-
-static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t *a);
-static int _4bri_get_serial_number(diva_os_xdi_adapter_t *a);
-static int diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                                  diva_xdi_um_cfg_cmd_t *cmd,
-                                  int length);
-static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t *a);
-static int diva_4bri_write_fpga_image(diva_os_xdi_adapter_t *a,
-                                     byte *data, dword length);
-static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter);
-static int diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
-                                      dword address,
-                                      const byte *data,
-                                      dword length, dword limit);
-static int diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
-                                  dword start_address, dword features);
-static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter);
-static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t *a);
-
-static int _4bri_is_rev_2_card(int card_ordinal)
-{
-       switch (card_ordinal) {
-       case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
-       case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
-       case CARDTYPE_DIVASRV_B_2M_V2_PCI:
-       case CARDTYPE_DIVASRV_B_2F_PCI:
-       case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
-               return (1);
-       }
-       return (0);
-}
-
-static int _4bri_is_rev_2_bri_card(int card_ordinal)
-{
-       switch (card_ordinal) {
-       case CARDTYPE_DIVASRV_B_2M_V2_PCI:
-       case CARDTYPE_DIVASRV_B_2F_PCI:
-       case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
-               return (1);
-       }
-       return (0);
-}
-
-static void diva_4bri_set_addresses(diva_os_xdi_adapter_t *a)
-{
-       dword offset = a->resources.pci.qoffset;
-       dword c_offset = offset * a->xdi_adapter.ControllerNumber;
-
-       a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 0;
-       a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 3;
-       a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 0;
-
-       /*
-         Set up hardware related pointers
-       */
-       a->xdi_adapter.Address = a->resources.pci.addr[2];      /* BAR2 SDRAM  */
-       a->xdi_adapter.Address += c_offset;
-
-       a->xdi_adapter.Control = a->resources.pci.addr[2];      /* BAR2 SDRAM  */
-
-       a->xdi_adapter.ram = a->resources.pci.addr[2];  /* BAR2 SDRAM  */
-       a->xdi_adapter.ram += c_offset + (offset - MQ_SHARED_RAM_SIZE);
-
-       a->xdi_adapter.reset = a->resources.pci.addr[0];        /* BAR0 CONFIG */
-       /*
-         ctlReg contains the register address for the MIPS CPU reset control
-       */
-       a->xdi_adapter.ctlReg = a->resources.pci.addr[3];       /* BAR3 CNTRL  */
-       /*
-         prom contains the register address for FPGA and EEPROM programming
-       */
-       a->xdi_adapter.prom = &a->xdi_adapter.reset[0x6E];
-}
-
-/*
-**  BAR0 - MEM - 0x100    - CONFIG MEM
-**  BAR1 - I/O - 0x100    - UNUSED
-**  BAR2 - MEM - MQ_MEMORY_SIZE (MQ2_MEMORY_SIZE on Rev.2) - SDRAM
-**  BAR3 - MEM - 0x2000 (0x10000 on Rev.2)   - CNTRL
-**
-**  Called by master adapter, that will initialize and add slave adapters
-*/
-int diva_4bri_init_card(diva_os_xdi_adapter_t *a)
-{
-       int bar, i;
-       byte __iomem *p;
-       PADAPTER_LIST_ENTRY quadro_list;
-       diva_os_xdi_adapter_t *diva_current;
-       diva_os_xdi_adapter_t *adapter_list[4];
-       PISDN_ADAPTER Slave;
-       unsigned long bar_length[ARRAY_SIZE(_4bri_bar_length)];
-       int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
-       int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
-       int factor = (tasks == 1) ? 1 : 2;
-
-       if (v2) {
-               if (_4bri_is_rev_2_bri_card(a->CardOrdinal)) {
-                       memcpy(bar_length, _4bri_v2_bri_bar_length,
-                              sizeof(bar_length));
-               } else {
-                       memcpy(bar_length, _4bri_v2_bar_length,
-                              sizeof(bar_length));
-               }
-       } else {
-               memcpy(bar_length, _4bri_bar_length, sizeof(bar_length));
-       }
-       DBG_TRC(("SDRAM_LENGTH=%08x, tasks=%d, factor=%d",
-                bar_length[2], tasks, factor))
-
-               /*
-                 Get Serial Number
-                 The serial number of 4BRI is accessible in accordance with PCI spec
-                 via command register located in configuration space, also we do not
-                 have to map any BAR before we can access it
-               */
-               if (!_4bri_get_serial_number(a)) {
-                       DBG_ERR(("A: 4BRI can't get Serial Number"))
-                               diva_4bri_cleanup_adapter(a);
-                       return (-1);
-               }
-
-       /*
-         Set properties
-       */
-       a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
-       DBG_LOG(("Load %s, SN:%ld, bus:%02x, func:%02x",
-                a->xdi_adapter.Properties.Name,
-                a->xdi_adapter.serialNo,
-                a->resources.pci.bus, a->resources.pci.func))
-
-               /*
-                 First initialization step: get and check hardware resoures.
-                 Do not map resources and do not access card at this step
-               */
-               for (bar = 0; bar < 4; bar++) {
-                       a->resources.pci.bar[bar] =
-                               divasa_get_pci_bar(a->resources.pci.bus,
-                                                  a->resources.pci.func, bar,
-                                                  a->resources.pci.hdev);
-                       if (!a->resources.pci.bar[bar]
-                           || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
-                               DBG_ERR(
-                                       ("A: invalid bar[%d]=%08x", bar,
-                                        a->resources.pci.bar[bar]))
-                                       return (-1);
-                       }
-               }
-       a->resources.pci.irq =
-               (byte) divasa_get_pci_irq(a->resources.pci.bus,
-                                         a->resources.pci.func,
-                                         a->resources.pci.hdev);
-       if (!a->resources.pci.irq) {
-               DBG_ERR(("A: invalid irq"));
-               return (-1);
-       }
-
-       a->xdi_adapter.sdram_bar = a->resources.pci.bar[2];
-
-       /*
-         Map all MEMORY BAR's
-       */
-       for (bar = 0; bar < 4; bar++) {
-               if (bar != 1) { /* ignore I/O */
-                       a->resources.pci.addr[bar] =
-                               divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
-                                                    bar_length[bar]);
-                       if (!a->resources.pci.addr[bar]) {
-                               DBG_ERR(("A: 4BRI: can't map bar[%d]", bar))
-                                       diva_4bri_cleanup_adapter(a);
-                               return (-1);
-                       }
-               }
-       }
-
-       /*
-         Register I/O port
-       */
-       sprintf(&a->port_name[0], "DIVA 4BRI %ld", (long) a->xdi_adapter.serialNo);
-
-       if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
-                                    bar_length[1], &a->port_name[0], 1)) {
-               DBG_ERR(("A: 4BRI: can't register bar[1]"))
-                       diva_4bri_cleanup_adapter(a);
-               return (-1);
-       }
-
-       a->resources.pci.addr[1] =
-               (void *) (unsigned long) a->resources.pci.bar[1];
-
-       /*
-         Set cleanup pointer for base adapter only, so slave adapter
-         will be unable to get cleanup
-       */
-       a->interface.cleanup_adapter_proc = diva_4bri_cleanup_adapter;
-
-       /*
-         Create slave adapters
-       */
-       if (tasks > 1) {
-               if (!(a->slave_adapters[0] =
-                     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
-               {
-                       diva_4bri_cleanup_adapter(a);
-                       return (-1);
-               }
-               if (!(a->slave_adapters[1] =
-                     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
-               {
-                       diva_os_free(0, a->slave_adapters[0]);
-                       a->slave_adapters[0] = NULL;
-                       diva_4bri_cleanup_adapter(a);
-                       return (-1);
-               }
-               if (!(a->slave_adapters[2] =
-                     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
-               {
-                       diva_os_free(0, a->slave_adapters[0]);
-                       diva_os_free(0, a->slave_adapters[1]);
-                       a->slave_adapters[0] = NULL;
-                       a->slave_adapters[1] = NULL;
-                       diva_4bri_cleanup_adapter(a);
-                       return (-1);
-               }
-               memset(a->slave_adapters[0], 0x00, sizeof(*a));
-               memset(a->slave_adapters[1], 0x00, sizeof(*a));
-               memset(a->slave_adapters[2], 0x00, sizeof(*a));
-       }
-
-       adapter_list[0] = a;
-       adapter_list[1] = a->slave_adapters[0];
-       adapter_list[2] = a->slave_adapters[1];
-       adapter_list[3] = a->slave_adapters[2];
-
-       /*
-         Allocate slave list
-       */
-       quadro_list =
-               (PADAPTER_LIST_ENTRY) diva_os_malloc(0, sizeof(*quadro_list));
-       if (!(a->slave_list = quadro_list)) {
-               for (i = 0; i < (tasks - 1); i++) {
-                       diva_os_free(0, a->slave_adapters[i]);
-                       a->slave_adapters[i] = NULL;
-               }
-               diva_4bri_cleanup_adapter(a);
-               return (-1);
-       }
-       memset(quadro_list, 0x00, sizeof(*quadro_list));
-
-       /*
-         Set interfaces
-       */
-       a->xdi_adapter.QuadroList = quadro_list;
-       for (i = 0; i < tasks; i++) {
-               adapter_list[i]->xdi_adapter.ControllerNumber = i;
-               adapter_list[i]->xdi_adapter.tasks = tasks;
-               quadro_list->QuadroAdapter[i] =
-                       &adapter_list[i]->xdi_adapter;
-       }
-
-       for (i = 0; i < tasks; i++) {
-               diva_current = adapter_list[i];
-
-               diva_current->dsp_mask = 0x00000003;
-
-               diva_current->xdi_adapter.a.io =
-                       &diva_current->xdi_adapter;
-               diva_current->xdi_adapter.DIRequest = request;
-               diva_current->interface.cmd_proc = diva_4bri_cmd_card_proc;
-               diva_current->xdi_adapter.Properties =
-                       CardProperties[a->CardOrdinal];
-               diva_current->CardOrdinal = a->CardOrdinal;
-
-               diva_current->xdi_adapter.Channels =
-                       CardProperties[a->CardOrdinal].Channels;
-               diva_current->xdi_adapter.e_max =
-                       CardProperties[a->CardOrdinal].E_info;
-               diva_current->xdi_adapter.e_tbl =
-                       diva_os_malloc(0,
-                                      diva_current->xdi_adapter.e_max *
-                                      sizeof(E_INFO));
-
-               if (!diva_current->xdi_adapter.e_tbl) {
-                       diva_4bri_cleanup_slave_adapters(a);
-                       diva_4bri_cleanup_adapter(a);
-                       for (i = 1; i < (tasks - 1); i++) {
-                               diva_os_free(0, adapter_list[i]);
-                       }
-                       return (-1);
-               }
-               memset(diva_current->xdi_adapter.e_tbl, 0x00,
-                      diva_current->xdi_adapter.e_max * sizeof(E_INFO));
-
-               if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.isr_spin_lock, "isr")) {
-                       diva_4bri_cleanup_slave_adapters(a);
-                       diva_4bri_cleanup_adapter(a);
-                       for (i = 1; i < (tasks - 1); i++) {
-                               diva_os_free(0, adapter_list[i]);
-                       }
-                       return (-1);
-               }
-               if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.data_spin_lock, "data")) {
-                       diva_4bri_cleanup_slave_adapters(a);
-                       diva_4bri_cleanup_adapter(a);
-                       for (i = 1; i < (tasks - 1); i++) {
-                               diva_os_free(0, adapter_list[i]);
-                       }
-                       return (-1);
-               }
-
-               strcpy(diva_current->xdi_adapter.req_soft_isr. dpc_thread_name, "kdivas4brid");
-
-               if (diva_os_initialize_soft_isr(&diva_current->xdi_adapter.req_soft_isr, DIDpcRoutine,
-                                               &diva_current->xdi_adapter)) {
-                       diva_4bri_cleanup_slave_adapters(a);
-                       diva_4bri_cleanup_adapter(a);
-                       for (i = 1; i < (tasks - 1); i++) {
-                               diva_os_free(0, adapter_list[i]);
-                       }
-                       return (-1);
-               }
-
-               /*
-                 Do not initialize second DPC - only one thread will be created
-               */
-               diva_current->xdi_adapter.isr_soft_isr.object =
-                       diva_current->xdi_adapter.req_soft_isr.object;
-       }
-
-       if (v2) {
-               prepare_qBri2_functions(&a->xdi_adapter);
-       } else {
-               prepare_qBri_functions(&a->xdi_adapter);
-       }
-
-       for (i = 0; i < tasks; i++) {
-               diva_current = adapter_list[i];
-               if (i)
-                       memcpy(&diva_current->resources, &a->resources, sizeof(divas_card_resources_t));
-               diva_current->resources.pci.qoffset = (a->xdi_adapter.MemorySize >> factor);
-       }
-
-       /*
-         Set up hardware related pointers
-       */
-       a->xdi_adapter.cfg = (void *) (unsigned long) a->resources.pci.bar[0];  /* BAR0 CONFIG */
-       a->xdi_adapter.port = (void *) (unsigned long) a->resources.pci.bar[1]; /* BAR1        */
-       a->xdi_adapter.ctlReg = (void *) (unsigned long) a->resources.pci.bar[3];       /* BAR3 CNTRL  */
-
-       for (i = 0; i < tasks; i++) {
-               diva_current = adapter_list[i];
-               diva_4bri_set_addresses(diva_current);
-               Slave = a->xdi_adapter.QuadroList->QuadroAdapter[i];
-               Slave->MultiMaster = &a->xdi_adapter;
-               Slave->sdram_bar = a->xdi_adapter.sdram_bar;
-               if (i) {
-                       Slave->serialNo = ((dword) (Slave->ControllerNumber << 24)) |
-                               a->xdi_adapter.serialNo;
-                       Slave->cardType = a->xdi_adapter.cardType;
-               }
-       }
-
-       /*
-         reset contains the base address for the PLX 9054 register set
-       */
-       p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
-       WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);   /* disable PCI interrupts */
-       DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
-
-       /*
-         Set IRQ handler
-       */
-       a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
-       sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA 4BRI %ld",
-               (long) a->xdi_adapter.serialNo);
-
-       if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
-                                a->xdi_adapter.irq_info.irq_name)) {
-               diva_4bri_cleanup_slave_adapters(a);
-               diva_4bri_cleanup_adapter(a);
-               for (i = 1; i < (tasks - 1); i++) {
-                       diva_os_free(0, adapter_list[i]);
-               }
-               return (-1);
-       }
-
-       a->xdi_adapter.irq_info.registered = 1;
-
-       /*
-         Add three slave adapters
-       */
-       if (tasks > 1) {
-               diva_add_slave_adapter(adapter_list[1]);
-               diva_add_slave_adapter(adapter_list[2]);
-               diva_add_slave_adapter(adapter_list[3]);
-       }
-
-       diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
-                     a->resources.pci.irq, a->xdi_adapter.serialNo);
-
-       return (0);
-}
-
-/*
-**  Cleanup function will be called for master adapter only
-**  this is guaranteed by design: cleanup callback is set
-**  by master adapter only
-*/
-static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t *a)
-{
-       int bar;
-
-       /*
-         Stop adapter if running
-       */
-       if (a->xdi_adapter.Initialized) {
-               diva_4bri_stop_adapter(a);
-       }
-
-       /*
-         Remove IRQ handler
-       */
-       if (a->xdi_adapter.irq_info.registered) {
-               diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
-       }
-       a->xdi_adapter.irq_info.registered = 0;
-
-       /*
-         Free DPC's and spin locks on all adapters
-       */
-       diva_4bri_cleanup_slave_adapters(a);
-
-       /*
-         Unmap all BARS
-       */
-       for (bar = 0; bar < 4; bar++) {
-               if (bar != 1) {
-                       if (a->resources.pci.bar[bar]
-                           && a->resources.pci.addr[bar]) {
-                               divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
-                               a->resources.pci.bar[bar] = 0;
-                               a->resources.pci.addr[bar] = NULL;
-                       }
-               }
-       }
-
-       /*
-         Unregister I/O
-       */
-       if (a->resources.pci.bar[1] && a->resources.pci.addr[1]) {
-               diva_os_register_io_port(a, 0, a->resources.pci.bar[1],
-                                        _4bri_is_rev_2_card(a->
-                                                            CardOrdinal) ?
-                                        _4bri_v2_bar_length[1] :
-                                        _4bri_bar_length[1],
-                                        &a->port_name[0], 1);
-               a->resources.pci.bar[1] = 0;
-               a->resources.pci.addr[1] = NULL;
-       }
-
-       if (a->slave_list) {
-               diva_os_free(0, a->slave_list);
-               a->slave_list = NULL;
-       }
-
-       return (0);
-}
-
-static int _4bri_get_serial_number(diva_os_xdi_adapter_t *a)
-{
-       dword data[64];
-       dword serNo;
-       word addr, status, i, j;
-       byte Bus, Slot;
-       void *hdev;
-
-       Bus = a->resources.pci.bus;
-       Slot = a->resources.pci.func;
-       hdev = a->resources.pci.hdev;
-
-       for (i = 0; i < 64; ++i) {
-               addr = i * 4;
-               for (j = 0; j < 5; ++j) {
-                       PCIwrite(Bus, Slot, 0x4E, &addr, sizeof(addr),
-                                hdev);
-                       diva_os_wait(1);
-                       PCIread(Bus, Slot, 0x4E, &status, sizeof(status),
-                               hdev);
-                       if (status & 0x8000)
-                               break;
-               }
-               if (j >= 5) {
-                       DBG_ERR(("EEPROM[%d] read failed (0x%x)", i * 4, addr))
-                               return (0);
-               }
-               PCIread(Bus, Slot, 0x50, &data[i], sizeof(data[i]), hdev);
-       }
-       DBG_BLK(((char *) &data[0], sizeof(data)))
-
-               serNo = data[32];
-       if (serNo == 0 || serNo == 0xffffffff)
-               serNo = data[63];
-
-       if (!serNo) {
-               DBG_LOG(("W: Serial Number == 0, create one serial number"));
-               serNo = a->resources.pci.bar[1] & 0xffff0000;
-               serNo |= a->resources.pci.bus << 8;
-               serNo |= a->resources.pci.func;
-       }
-
-       a->xdi_adapter.serialNo = serNo;
-
-       DBG_REG(("Serial No.          : %ld", a->xdi_adapter.serialNo))
-
-               return (serNo);
-}
-
-/*
-**  Release resources of slave adapters
-*/
-static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t *a)
-{
-       diva_os_xdi_adapter_t *adapter_list[4];
-       diva_os_xdi_adapter_t *diva_current;
-       int i;
-
-       adapter_list[0] = a;
-       adapter_list[1] = a->slave_adapters[0];
-       adapter_list[2] = a->slave_adapters[1];
-       adapter_list[3] = a->slave_adapters[2];
-
-       for (i = 0; i < a->xdi_adapter.tasks; i++) {
-               diva_current = adapter_list[i];
-               if (diva_current) {
-                       diva_os_destroy_spin_lock(&diva_current->
-                                                 xdi_adapter.
-                                                 isr_spin_lock, "unload");
-                       diva_os_destroy_spin_lock(&diva_current->
-                                                 xdi_adapter.
-                                                 data_spin_lock,
-                                                 "unload");
-
-                       diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
-                                               req_soft_isr);
-                       diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
-                                               isr_soft_isr);
-
-                       diva_os_remove_soft_isr(&diva_current->xdi_adapter.
-                                               req_soft_isr);
-                       diva_current->xdi_adapter.isr_soft_isr.object = NULL;
-
-                       if (diva_current->xdi_adapter.e_tbl) {
-                               diva_os_free(0,
-                                            diva_current->xdi_adapter.
-                                            e_tbl);
-                       }
-                       diva_current->xdi_adapter.e_tbl = NULL;
-                       diva_current->xdi_adapter.e_max = 0;
-                       diva_current->xdi_adapter.e_count = 0;
-               }
-       }
-
-       return (0);
-}
-
-static int
-diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                       diva_xdi_um_cfg_cmd_t *cmd, int length)
-{
-       int ret = -1;
-
-       if (cmd->adapter != a->controller) {
-               DBG_ERR(("A: 4bri_cmd, invalid controller=%d != %d",
-                        cmd->adapter, a->controller))
-                       return (-1);
-       }
-
-       switch (cmd->command) {
-       case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->CardOrdinal;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->xdi_adapter.serialNo;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       /*
-                         Only master adapter can access hardware config
-                       */
-                       a->xdi_mbox.data_length = sizeof(dword) * 9;
-                       a->xdi_mbox.data =
-                               diva_os_malloc(0, a->xdi_mbox.data_length);
-                       if (a->xdi_mbox.data) {
-                               int i;
-                               dword *data = (dword *) a->xdi_mbox.data;
-
-                               for (i = 0; i < 8; i++) {
-                                       *data++ = a->resources.pci.bar[i];
-                               }
-                               *data++ = (dword) a->resources.pci.irq;
-                               a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                               ret = 0;
-                       }
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_CARD_STATE:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       a->xdi_mbox.data_length = sizeof(dword);
-                       a->xdi_mbox.data =
-                               diva_os_malloc(0, a->xdi_mbox.data_length);
-                       if (a->xdi_mbox.data) {
-                               dword *data = (dword *) a->xdi_mbox.data;
-                               if (!a->xdi_adapter.ram
-                                   || !a->xdi_adapter.reset
-                                   || !a->xdi_adapter.cfg) {
-                                       *data = 3;
-                               } else if (a->xdi_adapter.trapped) {
-                                       *data = 2;
-                               } else if (a->xdi_adapter.Initialized) {
-                                       *data = 1;
-                               } else {
-                                       *data = 0;
-                               }
-                               a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                               ret = 0;
-                       }
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_WRITE_FPGA:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       ret =
-                               diva_4bri_write_fpga_image(a,
-                                                          (byte *)&cmd[1],
-                                                          cmd->command_data.
-                                                          write_fpga.
-                                                          image_length);
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_RESET_ADAPTER:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       ret = diva_4bri_reset_adapter(&a->xdi_adapter);
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       ret = diva_4bri_write_sdram_block(&a->xdi_adapter,
-                                                         cmd->
-                                                         command_data.
-                                                         write_sdram.
-                                                         offset,
-                                                         (byte *) &
-                                                         cmd[1],
-                                                         cmd->
-                                                         command_data.
-                                                         write_sdram.
-                                                         length,
-                                                         a->xdi_adapter.
-                                                         MemorySize);
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_START_ADAPTER:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       ret = diva_4bri_start_adapter(&a->xdi_adapter,
-                                                     cmd->command_data.
-                                                     start.offset,
-                                                     cmd->command_data.
-                                                     start.features);
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       a->xdi_adapter.features =
-                               cmd->command_data.features.features;
-                       a->xdi_adapter.a.protocol_capabilities =
-                               a->xdi_adapter.features;
-                       DBG_TRC(("Set raw protocol features (%08x)",
-                                a->xdi_adapter.features))
-                               ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_STOP_ADAPTER:
-               if (!a->xdi_adapter.ControllerNumber) {
-                       ret = diva_4bri_stop_adapter(a);
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
-               ret = diva_card_read_xlog(a);
-               break;
-
-       case DIVA_XDI_UM_CMD_READ_SDRAM:
-               if (!a->xdi_adapter.ControllerNumber
-                   && a->xdi_adapter.Address) {
-                       if (
-                               (a->xdi_mbox.data_length =
-                                cmd->command_data.read_sdram.length)) {
-                               if (
-                                       (a->xdi_mbox.data_length +
-                                        cmd->command_data.read_sdram.offset) <
-                                       a->xdi_adapter.MemorySize) {
-                                       a->xdi_mbox.data =
-                                               diva_os_malloc(0,
-                                                              a->xdi_mbox.
-                                                              data_length);
-                                       if (a->xdi_mbox.data) {
-                                               byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
-                                               byte __iomem *src = p;
-                                               byte *dst = a->xdi_mbox.data;
-                                               dword len = a->xdi_mbox.data_length;
-
-                                               src += cmd->command_data.read_sdram.offset;
-
-                                               while (len--) {
-                                                       *dst++ = READ_BYTE(src++);
-                                               }
-                                               DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
-                                               a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                                               ret = 0;
-                                       }
-                               }
-                       }
-               }
-               break;
-
-       default:
-               DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
-                        cmd->command))
-                       }
-
-       return (ret);
-}
-
-void *xdiLoadFile(char *FileName, dword *FileLength,
-                 unsigned long lim)
-{
-       void *ret = diva_xdiLoadFileFile;
-
-       if (FileLength) {
-               *FileLength = diva_xdiLoadFileLength;
-       }
-       diva_xdiLoadFileFile = NULL;
-       diva_xdiLoadFileLength = 0;
-
-       return (ret);
-}
-
-void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter)
-{
-}
-
-void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter)
-{
-}
-
-static int
-diva_4bri_write_fpga_image(diva_os_xdi_adapter_t *a, byte *data,
-                          dword length)
-{
-       int ret;
-
-       diva_xdiLoadFileFile = data;
-       diva_xdiLoadFileLength = length;
-
-       ret = qBri_FPGA_download(&a->xdi_adapter);
-
-       diva_xdiLoadFileFile = NULL;
-       diva_xdiLoadFileLength = 0;
-
-       return (ret ? 0 : -1);
-}
-
-static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter)
-{
-       PISDN_ADAPTER Slave;
-       int i;
-
-       if (!IoAdapter->Address || !IoAdapter->reset) {
-               return (-1);
-       }
-       if (IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't reset 4BRI adapter - please stop first",
-                        IoAdapter->ANum))
-                       return (-1);
-       }
-
-       /*
-         Forget all entities on all adapters
-       */
-       for (i = 0; ((i < IoAdapter->tasks) && IoAdapter->QuadroList); i++) {
-               Slave = IoAdapter->QuadroList->QuadroAdapter[i];
-               Slave->e_count = 0;
-               if (Slave->e_tbl) {
-                       memset(Slave->e_tbl, 0x00,
-                              Slave->e_max * sizeof(E_INFO));
-               }
-               Slave->head = 0;
-               Slave->tail = 0;
-               Slave->assign = 0;
-               Slave->trapped = 0;
-
-               memset(&Slave->a.IdTable[0], 0x00,
-                      sizeof(Slave->a.IdTable));
-               memset(&Slave->a.IdTypeTable[0], 0x00,
-                      sizeof(Slave->a.IdTypeTable));
-               memset(&Slave->a.FlowControlIdTable[0], 0x00,
-                      sizeof(Slave->a.FlowControlIdTable));
-               memset(&Slave->a.FlowControlSkipTable[0], 0x00,
-                      sizeof(Slave->a.FlowControlSkipTable));
-               memset(&Slave->a.misc_flags_table[0], 0x00,
-                      sizeof(Slave->a.misc_flags_table));
-               memset(&Slave->a.rx_stream[0], 0x00,
-                      sizeof(Slave->a.rx_stream));
-               memset(&Slave->a.tx_stream[0], 0x00,
-                      sizeof(Slave->a.tx_stream));
-               memset(&Slave->a.tx_pos[0], 0x00, sizeof(Slave->a.tx_pos));
-               memset(&Slave->a.rx_pos[0], 0x00, sizeof(Slave->a.rx_pos));
-       }
-
-       return (0);
-}
-
-
-static int
-diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
-                           dword address,
-                           const byte *data, dword length, dword limit)
-{
-       byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       byte __iomem *mem = p;
-
-       if (((address + length) >= limit) || !mem) {
-               DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
-               DBG_ERR(("A: A(%d) write 4BRI address=0x%08lx",
-                        IoAdapter->ANum, address + length))
-                       return (-1);
-       }
-       mem += address;
-
-       while (length--) {
-               WRITE_BYTE(mem++, *data++);
-       }
-
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
-       return (0);
-}
-
-static int
-diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
-                       dword start_address, dword features)
-{
-       volatile word __iomem *signature;
-       int started = 0;
-       int i;
-       byte __iomem *p;
-
-       /*
-         start adapter
-       */
-       start_qBri_hardware(IoAdapter);
-
-       p = DIVA_OS_MEM_ATTACH_RAM(IoAdapter);
-       /*
-         wait for signature in shared memory (max. 3 seconds)
-       */
-       signature = (volatile word __iomem *) (&p[0x1E]);
-
-       for (i = 0; i < 300; ++i) {
-               diva_os_wait(10);
-               if (READ_WORD(&signature[0]) == 0x4447) {
-                       DBG_TRC(("Protocol startup time %d.%02d seconds",
-                                (i / 100), (i % 100)))
-                               started = 1;
-                       break;
-               }
-       }
-
-       for (i = 1; i < IoAdapter->tasks; i++) {
-               IoAdapter->QuadroList->QuadroAdapter[i]->features =
-                       IoAdapter->features;
-               IoAdapter->QuadroList->QuadroAdapter[i]->a.
-                       protocol_capabilities = IoAdapter->features;
-       }
-
-       if (!started) {
-               DBG_FTL(("%s: Adapter selftest failed, signature=%04x",
-                        IoAdapter->Properties.Name,
-                        READ_WORD(&signature[0])))
-                       DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
-               (*(IoAdapter->trapFnc)) (IoAdapter);
-               IoAdapter->stop(IoAdapter);
-               return (-1);
-       }
-       DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
-
-       for (i = 0; i < IoAdapter->tasks; i++) {
-               IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 1;
-               IoAdapter->QuadroList->QuadroAdapter[i]->IrqCount = 0;
-       }
-
-       if (check_qBri_interrupt(IoAdapter)) {
-               DBG_ERR(("A: A(%d) interrupt test failed",
-                        IoAdapter->ANum))
-                       for (i = 0; i < IoAdapter->tasks; i++) {
-                               IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
-                       }
-               IoAdapter->stop(IoAdapter);
-               return (-1);
-       }
-
-       IoAdapter->Properties.Features = (word) features;
-       diva_xdi_display_adapter_features(IoAdapter->ANum);
-
-       for (i = 0; i < IoAdapter->tasks; i++) {
-               DBG_LOG(("A(%d) %s adapter successfully started",
-                        IoAdapter->QuadroList->QuadroAdapter[i]->ANum,
-                        (IoAdapter->tasks == 1) ? "BRI 2.0" : "4BRI"))
-                       diva_xdi_didd_register_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
-               IoAdapter->QuadroList->QuadroAdapter[i]->Properties.Features = (word) features;
-       }
-
-       return (0);
-}
-
-static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter)
-{
-#ifdef SUPPORT_INTERRUPT_TEST_ON_4BRI
-       int i;
-       ADAPTER *a = &IoAdapter->a;
-       byte __iomem *p;
-
-       IoAdapter->IrqCount = 0;
-
-       if (IoAdapter->ControllerNumber > 0)
-               return (-1);
-
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-       /*
-         interrupt test
-       */
-       a->ReadyInt = 1;
-       a->ram_out(a, &PR_RAM->ReadyInt, 1);
-
-       for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
-
-       return ((IoAdapter->IrqCount > 0) ? 0 : -1);
-#else
-       dword volatile __iomem *qBriIrq;
-       byte __iomem *p;
-       /*
-         Reset on-board interrupt register
-       */
-       IoAdapter->IrqCount = 0;
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriIrq = (dword volatile __iomem *) (&p[_4bri_is_rev_2_card
-                                                (IoAdapter->
-                                                 cardType) ? (MQ2_BREG_IRQ_TEST)
-                                                : (MQ_BREG_IRQ_TEST)]);
-
-       WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-
-       diva_os_wait(100);
-
-       return (0);
-#endif                         /* SUPPORT_INTERRUPT_TEST_ON_4BRI */
-}
-
-static void diva_4bri_clear_interrupts(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-
-       /*
-         clear any pending interrupt
-       */
-       IoAdapter->disIrq(IoAdapter);
-
-       IoAdapter->tst_irq(&IoAdapter->a);
-       IoAdapter->clr_irq(&IoAdapter->a);
-       IoAdapter->tst_irq(&IoAdapter->a);
-
-       /*
-         kill pending dpcs
-       */
-       diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
-       diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
-}
-
-static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-       int i;
-
-       if (!IoAdapter->ram) {
-               return (-1);
-       }
-
-       if (!IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
-                        IoAdapter->ANum))
-                       return (-1);    /* nothing to stop */
-       }
-
-       for (i = 0; i < IoAdapter->tasks; i++) {
-               IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
-       }
-
-       /*
-         Disconnect Adapters from DIDD
-       */
-       for (i = 0; i < IoAdapter->tasks; i++) {
-               diva_xdi_didd_remove_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
-       }
-
-       i = 100;
-
-       /*
-         Stop interrupts
-       */
-       a->clear_interrupts_proc = diva_4bri_clear_interrupts;
-       IoAdapter->a.ReadyInt = 1;
-       IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
-       do {
-               diva_os_sleep(10);
-       } while (i-- && a->clear_interrupts_proc);
-
-       if (a->clear_interrupts_proc) {
-               diva_4bri_clear_interrupts(a);
-               a->clear_interrupts_proc = NULL;
-               DBG_ERR(("A: A(%d) no final interrupt from 4BRI adapter",
-                        IoAdapter->ANum))
-                       }
-       IoAdapter->a.ReadyInt = 0;
-
-       /*
-         Stop and reset adapter
-       */
-       IoAdapter->stop(IoAdapter);
-
-       return (0);
-}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.h b/drivers/isdn/hardware/eicon/os_4bri.h
deleted file mode 100644 (file)
index 94b2709..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: os_4bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
-
-#ifndef __DIVA_OS_4_BRI_H__
-#define __DIVA_OS_4_BRI_H__
-
-int diva_4bri_init_card(diva_os_xdi_adapter_t *a);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/os_bri.c b/drivers/isdn/hardware/eicon/os_bri.c
deleted file mode 100644 (file)
index de93090..0000000
+++ /dev/null
@@ -1,815 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* $Id: os_bri.c,v 1.21 2004/03/21 17:26:01 armin Exp $ */
-
-#include "platform.h"
-#include "debuglib.h"
-#include "cardtype.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di_defs.h"
-#include "dsp_defs.h"
-#include "di.h"
-#include "io.h"
-
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "os_bri.h"
-#include "diva_pci.h"
-#include "mi_pc.h"
-#include "pc_maint.h"
-#include "dsrv_bri.h"
-
-/*
-**  IMPORTS
-*/
-extern void prepare_maestra_functions(PISDN_ADAPTER IoAdapter);
-extern void diva_xdi_display_adapter_features(int card);
-extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a);
-
-/*
-**  LOCALS
-*/
-static int bri_bar_length[3] = {
-       0x80,
-       0x80,
-       0x20
-};
-static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t *a);
-static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t *a);
-static int diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                                 diva_xdi_um_cfg_cmd_t *cmd, int length);
-static int diva_bri_reregister_io(diva_os_xdi_adapter_t *a);
-static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter);
-static int diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
-                                     dword address,
-                                     const byte *data, dword length);
-static int diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
-                                 dword start_address, dword features);
-static int diva_bri_stop_adapter(diva_os_xdi_adapter_t *a);
-
-static void diva_bri_set_addresses(diva_os_xdi_adapter_t *a)
-{
-       a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
-       a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 1;
-       a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 1;
-       a->resources.pci.mem_type_id[MEM_TYPE_PORT] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 2;
-
-       a->xdi_adapter.ram = a->resources.pci.addr[0];
-       a->xdi_adapter.cfg = a->resources.pci.addr[1];
-       a->xdi_adapter.Address = a->resources.pci.addr[2];
-
-       a->xdi_adapter.reset = a->xdi_adapter.cfg;
-       a->xdi_adapter.port = a->xdi_adapter.Address;
-
-       a->xdi_adapter.ctlReg = a->xdi_adapter.port + M_PCI_RESET;
-
-       a->xdi_adapter.reset += 0x4C;   /* PLX 9050 !! */
-}
-
-/*
-**  BAR0 - MEM Addr  - 0x80  - NOT USED
-**  BAR1 - I/O Addr  - 0x80
-**  BAR2 - I/O Addr  - 0x20
-*/
-int diva_bri_init_card(diva_os_xdi_adapter_t *a)
-{
-       int bar;
-       dword bar2 = 0, bar2_length = 0xffffffff;
-       word cmd = 0, cmd_org;
-       byte Bus, Slot;
-       void *hdev;
-       byte __iomem *p;
-
-       /*
-         Set properties
-       */
-       a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
-       DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
-
-               /*
-                 Get resources
-               */
-               for (bar = 0; bar < 3; bar++) {
-                       a->resources.pci.bar[bar] =
-                               divasa_get_pci_bar(a->resources.pci.bus,
-                                                  a->resources.pci.func, bar,
-                                                  a->resources.pci.hdev);
-                       if (!a->resources.pci.bar[bar]) {
-                               DBG_ERR(("A: can't get BAR[%d]", bar))
-                                       return (-1);
-                       }
-               }
-
-       a->resources.pci.irq =
-               (byte) divasa_get_pci_irq(a->resources.pci.bus,
-                                         a->resources.pci.func,
-                                         a->resources.pci.hdev);
-       if (!a->resources.pci.irq) {
-               DBG_ERR(("A: invalid irq"));
-               return (-1);
-       }
-
-       /*
-         Get length of I/O bar 2 - it is different by older
-         EEPROM version
-       */
-       Bus = a->resources.pci.bus;
-       Slot = a->resources.pci.func;
-       hdev = a->resources.pci.hdev;
-
-       /*
-         Get plain original values of the BAR2 CDM registers
-       */
-       PCIread(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
-       PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
-       /*
-         Disable device and get BAR2 length
-       */
-       PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
-       PCIwrite(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
-       PCIread(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
-       /*
-         Restore BAR2 and CMD registers
-       */
-       PCIwrite(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
-       PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
-
-       /*
-         Calculate BAR2 length
-       */
-       bar2_length = (~(bar2_length & ~7)) + 1;
-       DBG_LOG(("BAR[2] length=%lx", bar2_length))
-
-               /*
-                 Map and register resources
-               */
-               if (!(a->resources.pci.addr[0] =
-                     divasa_remap_pci_bar(a, 0, a->resources.pci.bar[0],
-                                          bri_bar_length[0]))) {
-                       DBG_ERR(("A: BRI, can't map BAR[0]"))
-                               diva_bri_cleanup_adapter(a);
-                       return (-1);
-               }
-
-       sprintf(&a->port_name[0], "BRI %02x:%02x",
-               a->resources.pci.bus, a->resources.pci.func);
-
-       if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
-                                    bri_bar_length[1], &a->port_name[0], 1)) {
-               DBG_ERR(("A: BRI, can't register BAR[1]"))
-                       diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       a->resources.pci.addr[1] = (void *) (unsigned long) a->resources.pci.bar[1];
-       a->resources.pci.length[1] = bri_bar_length[1];
-
-       if (diva_os_register_io_port(a, 1, a->resources.pci.bar[2],
-                                    bar2_length, &a->port_name[0], 2)) {
-               DBG_ERR(("A: BRI, can't register BAR[2]"))
-                       diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       a->resources.pci.addr[2] = (void *) (unsigned long) a->resources.pci.bar[2];
-       a->resources.pci.length[2] = bar2_length;
-
-       /*
-         Set all memory areas
-       */
-       diva_bri_set_addresses(a);
-
-       /*
-         Get Serial Number
-       */
-       a->xdi_adapter.serialNo = diva_bri_get_serial_number(a);
-
-       /*
-         Register I/O ports with correct name now
-       */
-       if (diva_bri_reregister_io(a)) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-
-       /*
-         Initialize OS dependent objects
-       */
-       if (diva_os_initialize_spin_lock
-           (&a->xdi_adapter.isr_spin_lock, "isr")) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       if (diva_os_initialize_spin_lock
-           (&a->xdi_adapter.data_spin_lock, "data")) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-
-       strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasbrid");
-
-       if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
-                                       DIDpcRoutine, &a->xdi_adapter)) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       /*
-         Do not initialize second DPC - only one thread will be created
-       */
-       a->xdi_adapter.isr_soft_isr.object = a->xdi_adapter.req_soft_isr.object;
-
-       /*
-         Create entity table
-       */
-       a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
-       a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
-       a->xdi_adapter.e_tbl = diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
-       if (!a->xdi_adapter.e_tbl) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
-
-       /*
-         Set up interface
-       */
-       a->xdi_adapter.a.io = &a->xdi_adapter;
-       a->xdi_adapter.DIRequest = request;
-       a->interface.cleanup_adapter_proc = diva_bri_cleanup_adapter;
-       a->interface.cmd_proc = diva_bri_cmd_card_proc;
-
-       p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
-       outpp(p, 0x41);
-       DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
-
-       prepare_maestra_functions(&a->xdi_adapter);
-
-       a->dsp_mask = 0x00000003;
-
-       /*
-         Set IRQ handler
-       */
-       a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
-       sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA BRI %ld",
-               (long) a->xdi_adapter.serialNo);
-       if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
-                                a->xdi_adapter.irq_info.irq_name)) {
-               diva_bri_cleanup_adapter(a);
-               return (-1);
-       }
-       a->xdi_adapter.irq_info.registered = 1;
-
-       diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
-                     a->resources.pci.irq, a->xdi_adapter.serialNo);
-
-       return (0);
-}
-
-
-static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t *a)
-{
-       int i;
-
-       if (a->xdi_adapter.Initialized) {
-               diva_bri_stop_adapter(a);
-       }
-
-       /*
-         Remove ISR Handler
-       */
-       if (a->xdi_adapter.irq_info.registered) {
-               diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
-       }
-       a->xdi_adapter.irq_info.registered = 0;
-
-       if (a->resources.pci.addr[0] && a->resources.pci.bar[0]) {
-               divasa_unmap_pci_bar(a->resources.pci.addr[0]);
-               a->resources.pci.addr[0] = NULL;
-               a->resources.pci.bar[0] = 0;
-       }
-
-       for (i = 1; i < 3; i++) {
-               if (a->resources.pci.addr[i] && a->resources.pci.bar[i]) {
-                       diva_os_register_io_port(a, 0,
-                                                a->resources.pci.bar[i],
-                                                a->resources.pci.
-                                                length[i],
-                                                &a->port_name[0], i);
-                       a->resources.pci.addr[i] = NULL;
-                       a->resources.pci.bar[i] = 0;
-               }
-       }
-
-       /*
-         Free OS objects
-       */
-       diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
-       diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
-
-       diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
-       a->xdi_adapter.isr_soft_isr.object = NULL;
-
-       diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
-       diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
-
-       /*
-         Free memory
-       */
-       if (a->xdi_adapter.e_tbl) {
-               diva_os_free(0, a->xdi_adapter.e_tbl);
-               a->xdi_adapter.e_tbl = NULL;
-       }
-
-       return (0);
-}
-
-void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter)
-{
-}
-
-/*
-**  Get serial number
-*/
-static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t *a)
-{
-       dword serNo = 0;
-       byte __iomem *confIO;
-       word serHi, serLo;
-       word __iomem *confMem;
-
-       confIO = DIVA_OS_MEM_ATTACH_CFG(&a->xdi_adapter);
-       serHi = (word) (inppw(&confIO[0x22]) & 0x0FFF);
-       serLo = (word) (inppw(&confIO[0x26]) & 0x0FFF);
-       serNo = ((dword) serHi << 16) | (dword) serLo;
-       DIVA_OS_MEM_DETACH_CFG(&a->xdi_adapter, confIO);
-
-       if ((serNo == 0) || (serNo == 0xFFFFFFFF)) {
-               DBG_FTL(("W: BRI use BAR[0] to get card serial number"))
-
-                       confMem = (word __iomem *)DIVA_OS_MEM_ATTACH_RAM(&a->xdi_adapter);
-               serHi = (word) (READ_WORD(&confMem[0x11]) & 0x0FFF);
-               serLo = (word) (READ_WORD(&confMem[0x13]) & 0x0FFF);
-               serNo = (((dword) serHi) << 16) | ((dword) serLo);
-               DIVA_OS_MEM_DETACH_RAM(&a->xdi_adapter, confMem);
-       }
-
-       DBG_LOG(("Serial Number=%ld", serNo))
-
-               return (serNo);
-}
-
-/*
-**  Unregister I/O and register it with new name,
-**  based on Serial Number
-*/
-static int diva_bri_reregister_io(diva_os_xdi_adapter_t *a)
-{
-       int i;
-
-       for (i = 1; i < 3; i++) {
-               diva_os_register_io_port(a, 0, a->resources.pci.bar[i],
-                                        a->resources.pci.length[i],
-                                        &a->port_name[0], i);
-               a->resources.pci.addr[i] = NULL;
-       }
-
-       sprintf(a->port_name, "DIVA BRI %ld",
-               (long) a->xdi_adapter.serialNo);
-
-       for (i = 1; i < 3; i++) {
-               if (diva_os_register_io_port(a, 1, a->resources.pci.bar[i],
-                                            a->resources.pci.length[i],
-                                            &a->port_name[0], i)) {
-                       DBG_ERR(("A: failed to reregister BAR[%d]", i))
-                               return (-1);
-               }
-               a->resources.pci.addr[i] =
-                       (void *) (unsigned long) a->resources.pci.bar[i];
-       }
-
-       return (0);
-}
-
-/*
-**  Process command from user mode
-*/
-static int
-diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                      diva_xdi_um_cfg_cmd_t *cmd, int length)
-{
-       int ret = -1;
-
-       if (cmd->adapter != a->controller) {
-               DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
-                        cmd->adapter, a->controller))
-                       return (-1);
-       }
-
-       switch (cmd->command) {
-       case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->CardOrdinal;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->xdi_adapter.serialNo;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
-               a->xdi_mbox.data_length = sizeof(dword) * 9;
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       int i;
-                       dword *data = (dword *) a->xdi_mbox.data;
-
-                       for (i = 0; i < 8; i++) {
-                               *data++ = a->resources.pci.bar[i];
-                       }
-                       *data++ = (dword) a->resources.pci.irq;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_CARD_STATE:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       dword *data = (dword *) a->xdi_mbox.data;
-                       if (!a->xdi_adapter.port) {
-                               *data = 3;
-                       } else if (a->xdi_adapter.trapped) {
-                               *data = 2;
-                       } else if (a->xdi_adapter.Initialized) {
-                               *data = 1;
-                       } else {
-                               *data = 0;
-                       }
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_RESET_ADAPTER:
-               ret = diva_bri_reset_adapter(&a->xdi_adapter);
-               break;
-
-       case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
-               ret = diva_bri_write_sdram_block(&a->xdi_adapter,
-                                                cmd->command_data.
-                                                write_sdram.offset,
-                                                (byte *)&cmd[1],
-                                                cmd->command_data.
-                                                write_sdram.length);
-               break;
-
-       case DIVA_XDI_UM_CMD_START_ADAPTER:
-               ret = diva_bri_start_adapter(&a->xdi_adapter,
-                                            cmd->command_data.start.
-                                            offset,
-                                            cmd->command_data.start.
-                                            features);
-               break;
-
-       case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
-               a->xdi_adapter.features =
-                       cmd->command_data.features.features;
-               a->xdi_adapter.a.protocol_capabilities =
-                       a->xdi_adapter.features;
-               DBG_TRC(
-                       ("Set raw protocol features (%08x)",
-                        a->xdi_adapter.features)) ret = 0;
-               break;
-
-       case DIVA_XDI_UM_CMD_STOP_ADAPTER:
-               ret = diva_bri_stop_adapter(a);
-               break;
-
-       case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
-               ret = diva_card_read_xlog(a);
-               break;
-
-       default:
-               DBG_ERR(
-                       ("A: A(%d) invalid cmd=%d", a->controller,
-                        cmd->command))}
-
-       return (ret);
-}
-
-static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter)
-{
-       byte __iomem *addrHi, *addrLo, *ioaddr;
-       dword i;
-       byte __iomem *Port;
-
-       if (!IoAdapter->port) {
-               return (-1);
-       }
-       if (IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't reset BRI adapter - please stop first",
-                        IoAdapter->ANum)) return (-1);
-       }
-       (*(IoAdapter->rstFnc)) (IoAdapter);
-       diva_os_wait(100);
-       Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
-       addrHi = Port +
-               ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
-       addrLo = Port + ADDR;
-       ioaddr = Port + DATA;
-       /*
-         recover
-       */
-       outpp(addrHi, (byte) 0);
-       outppw(addrLo, (word) 0);
-       outppw(ioaddr, (word) 0);
-       /*
-         clear shared memory
-       */
-       outpp(addrHi,
-             (byte) (
-                     (IoAdapter->MemoryBase + IoAdapter->MemorySize -
-                      BRI_SHARED_RAM_SIZE) >> 16));
-       outppw(addrLo, 0);
-       for (i = 0; i < 0x8000; outppw(ioaddr, 0), ++i);
-       diva_os_wait(100);
-
-       /*
-         clear signature
-       */
-       outpp(addrHi,
-             (byte) (
-                     (IoAdapter->MemoryBase + IoAdapter->MemorySize -
-                      BRI_SHARED_RAM_SIZE) >> 16));
-       outppw(addrLo, 0x1e);
-       outpp(ioaddr, 0);
-       outpp(ioaddr, 0);
-
-       outpp(addrHi, (byte) 0);
-       outppw(addrLo, (word) 0);
-       outppw(ioaddr, (word) 0);
-
-       DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
-
-       /*
-         Forget all outstanding entities
-       */
-       IoAdapter->e_count = 0;
-       if (IoAdapter->e_tbl) {
-               memset(IoAdapter->e_tbl, 0x00,
-                      IoAdapter->e_max * sizeof(E_INFO));
-       }
-       IoAdapter->head = 0;
-       IoAdapter->tail = 0;
-       IoAdapter->assign = 0;
-       IoAdapter->trapped = 0;
-
-       memset(&IoAdapter->a.IdTable[0], 0x00,
-              sizeof(IoAdapter->a.IdTable));
-       memset(&IoAdapter->a.IdTypeTable[0], 0x00,
-              sizeof(IoAdapter->a.IdTypeTable));
-       memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
-              sizeof(IoAdapter->a.FlowControlIdTable));
-       memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
-              sizeof(IoAdapter->a.FlowControlSkipTable));
-       memset(&IoAdapter->a.misc_flags_table[0], 0x00,
-              sizeof(IoAdapter->a.misc_flags_table));
-       memset(&IoAdapter->a.rx_stream[0], 0x00,
-              sizeof(IoAdapter->a.rx_stream));
-       memset(&IoAdapter->a.tx_stream[0], 0x00,
-              sizeof(IoAdapter->a.tx_stream));
-       memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
-       memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
-
-       return (0);
-}
-
-static int
-diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
-                          dword address, const byte *data, dword length)
-{
-       byte __iomem *addrHi, *addrLo, *ioaddr;
-       byte __iomem *Port;
-
-       if (!IoAdapter->port) {
-               return (-1);
-       }
-
-       Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
-       addrHi = Port +
-               ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
-       addrLo = Port + ADDR;
-       ioaddr = Port + DATA;
-
-       while (length--) {
-               outpp(addrHi, (word) (address >> 16));
-               outppw(addrLo, (word) (address & 0x0000ffff));
-               outpp(ioaddr, *data++);
-               address++;
-       }
-
-       DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
-       return (0);
-}
-
-static int
-diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
-                      dword start_address, dword features)
-{
-       byte __iomem *Port;
-       dword i, test;
-       byte __iomem *addrHi, *addrLo, *ioaddr;
-       int started = 0;
-       ADAPTER *a = &IoAdapter->a;
-
-       if (IoAdapter->Initialized) {
-               DBG_ERR(
-                       ("A: A(%d) bri_start_adapter, adapter already running",
-                        IoAdapter->ANum)) return (-1);
-       }
-       if (!IoAdapter->port) {
-               DBG_ERR(("A: A(%d) bri_start_adapter, adapter not mapped",
-                        IoAdapter->ANum)) return (-1);
-       }
-
-       sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
-       DBG_LOG(("A(%d) start BRI", IoAdapter->ANum))
-
-               Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
-       addrHi = Port +
-               ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
-       addrLo = Port + ADDR;
-       ioaddr = Port + DATA;
-
-       outpp(addrHi,
-             (byte) (
-                     (IoAdapter->MemoryBase + IoAdapter->MemorySize -
-                      BRI_SHARED_RAM_SIZE) >> 16));
-       outppw(addrLo, 0x1e);
-       outppw(ioaddr, 0x00);
-       DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
-
-       /*
-         start the protocol code
-       */
-       Port = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       outpp(Port, 0x08);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, Port);
-
-       Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
-       addrHi = Port +
-               ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
-       addrLo = Port + ADDR;
-       ioaddr = Port + DATA;
-       /*
-         wait for signature (max. 3 seconds)
-       */
-       for (i = 0; i < 300; ++i) {
-               diva_os_wait(10);
-               outpp(addrHi,
-                     (byte) (
-                             (IoAdapter->MemoryBase +
-                              IoAdapter->MemorySize -
-                              BRI_SHARED_RAM_SIZE) >> 16));
-               outppw(addrLo, 0x1e);
-               test = (dword) inppw(ioaddr);
-               if (test == 0x4447) {
-                       DBG_LOG(
-                               ("Protocol startup time %d.%02d seconds",
-                                (i / 100), (i % 100)))
-                               started = 1;
-                       break;
-               }
-       }
-       DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
-
-       if (!started) {
-               DBG_FTL(("A: A(%d) %s: Adapter selftest failed 0x%04X",
-                        IoAdapter->ANum, IoAdapter->Properties.Name,
-                        test))
-                       (*(IoAdapter->trapFnc)) (IoAdapter);
-               return (-1);
-       }
-
-       IoAdapter->Initialized = 1;
-
-       /*
-         Check Interrupt
-       */
-       IoAdapter->IrqCount = 0;
-       a->ReadyInt = 1;
-
-       if (IoAdapter->reset) {
-               Port = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-               outpp(Port, 0x41);
-               DIVA_OS_MEM_DETACH_RESET(IoAdapter, Port);
-       }
-
-       a->ram_out(a, &PR_RAM->ReadyInt, 1);
-       for (i = 0; ((!IoAdapter->IrqCount) && (i < 100)); i++) {
-               diva_os_wait(10);
-       }
-       if (!IoAdapter->IrqCount) {
-               DBG_ERR(
-                       ("A: A(%d) interrupt test failed",
-                        IoAdapter->ANum))
-                       IoAdapter->Initialized = 0;
-               IoAdapter->stop(IoAdapter);
-               return (-1);
-       }
-
-       IoAdapter->Properties.Features = (word) features;
-       diva_xdi_display_adapter_features(IoAdapter->ANum);
-       DBG_LOG(("A(%d) BRI adapter successfully started", IoAdapter->ANum))
-               /*
-                 Register with DIDD
-               */
-               diva_xdi_didd_register_adapter(IoAdapter->ANum);
-
-       return (0);
-}
-
-static void diva_bri_clear_interrupts(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-
-       /*
-         clear any pending interrupt
-       */
-       IoAdapter->disIrq(IoAdapter);
-
-       IoAdapter->tst_irq(&IoAdapter->a);
-       IoAdapter->clr_irq(&IoAdapter->a);
-       IoAdapter->tst_irq(&IoAdapter->a);
-
-       /*
-         kill pending dpcs
-       */
-       diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
-       diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
-}
-
-/*
-**  Stop card
-*/
-static int diva_bri_stop_adapter(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-       int i = 100;
-
-       if (!IoAdapter->port) {
-               return (-1);
-       }
-       if (!IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't stop BRI adapter - not running",
-                        IoAdapter->ANum))
-                       return (-1);    /* nothing to stop */
-       }
-       IoAdapter->Initialized = 0;
-
-       /*
-         Disconnect Adapter from DIDD
-       */
-       diva_xdi_didd_remove_adapter(IoAdapter->ANum);
-
-       /*
-         Stop interrupts
-       */
-       a->clear_interrupts_proc = diva_bri_clear_interrupts;
-       IoAdapter->a.ReadyInt = 1;
-       IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
-       do {
-               diva_os_sleep(10);
-       } while (i-- && a->clear_interrupts_proc);
-       if (a->clear_interrupts_proc) {
-               diva_bri_clear_interrupts(a);
-               a->clear_interrupts_proc = NULL;
-               DBG_ERR(("A: A(%d) no final interrupt from BRI adapter",
-                        IoAdapter->ANum))
-                       }
-       IoAdapter->a.ReadyInt = 0;
-
-       /*
-         Stop and reset adapter
-       */
-       IoAdapter->stop(IoAdapter);
-
-       return (0);
-}
diff --git a/drivers/isdn/hardware/eicon/os_bri.h b/drivers/isdn/hardware/eicon/os_bri.h
deleted file mode 100644 (file)
index 37c92cc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: os_bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
-
-#ifndef __DIVA_OS_BRI_REV_1_H__
-#define __DIVA_OS_BRI_REV_1_H__
-
-int diva_bri_init_card(diva_os_xdi_adapter_t *a);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/os_capi.h b/drivers/isdn/hardware/eicon/os_capi.h
deleted file mode 100644 (file)
index e72394b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* $Id: os_capi.h,v 1.7 2003/04/12 21:40:49 schindler Exp $
- *
- * ISDN interface module for Eicon active cards DIVA.
- * CAPI Interface OS include files
- *
- * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
- * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-#ifndef __OS_CAPI_H__
-#define __OS_CAPI_H__
-
-#include <linux/capi.h>
-#include <linux/kernelcapi.h>
-#include <linux/isdn/capiutil.h>
-#include <linux/isdn/capilli.h>
-
-#endif /* __OS_CAPI_H__ */
diff --git a/drivers/isdn/hardware/eicon/os_pri.c b/drivers/isdn/hardware/eicon/os_pri.c
deleted file mode 100644 (file)
index b20f1fb..0000000
+++ /dev/null
@@ -1,1053 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* $Id: os_pri.c,v 1.32 2004/03/21 17:26:01 armin Exp $ */
-
-#include "platform.h"
-#include "debuglib.h"
-#include "cardtype.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di_defs.h"
-#include "dsp_defs.h"
-#include "di.h"
-#include "io.h"
-
-#include "xdi_msg.h"
-#include "xdi_adapter.h"
-#include "os_pri.h"
-#include "diva_pci.h"
-#include "mi_pc.h"
-#include "pc_maint.h"
-#include "dsp_tst.h"
-#include "diva_dma.h"
-#include "dsrv_pri.h"
-
-/* --------------------------------------------------------------------------
-   OS Dependent part of XDI driver for DIVA PRI Adapter
-
-   DSP detection/validation by Anthony Booth (Eicon Networks, www.eicon.com)
-   -------------------------------------------------------------------------- */
-
-#define DIVA_PRI_NO_PCI_BIOS_WORKAROUND 1
-
-extern int diva_card_read_xlog(diva_os_xdi_adapter_t *a);
-
-/*
-**  IMPORTS
-*/
-extern void prepare_pri_functions(PISDN_ADAPTER IoAdapter);
-extern void prepare_pri2_functions(PISDN_ADAPTER IoAdapter);
-extern void diva_xdi_display_adapter_features(int card);
-
-static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t *a);
-static int diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                                 diva_xdi_um_cfg_cmd_t *cmd, int length);
-static int pri_get_serial_number(diva_os_xdi_adapter_t *a);
-static int diva_pri_stop_adapter(diva_os_xdi_adapter_t *a);
-static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t *a);
-
-/*
-**  Check card revision
-*/
-static int pri_is_rev_2_card(int card_ordinal)
-{
-       switch (card_ordinal) {
-       case CARDTYPE_DIVASRV_P_30M_V2_PCI:
-       case CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI:
-               return (1);
-       }
-       return (0);
-}
-
-static void diva_pri_set_addresses(diva_os_xdi_adapter_t *a)
-{
-       a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 0;
-       a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_CONFIG] = 4;
-       a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
-       a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 2;
-       a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 4;
-       a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 3;
-
-       a->xdi_adapter.Address = a->resources.pci.addr[0];
-       a->xdi_adapter.Control = a->resources.pci.addr[2];
-       a->xdi_adapter.Config = a->resources.pci.addr[4];
-
-       a->xdi_adapter.ram = a->resources.pci.addr[0];
-       a->xdi_adapter.ram += MP_SHARED_RAM_OFFSET;
-
-       a->xdi_adapter.reset = a->resources.pci.addr[2];
-       a->xdi_adapter.reset += MP_RESET;
-
-       a->xdi_adapter.cfg = a->resources.pci.addr[4];
-       a->xdi_adapter.cfg += MP_IRQ_RESET;
-
-       a->xdi_adapter.sdram_bar = a->resources.pci.bar[0];
-
-       a->xdi_adapter.prom = a->resources.pci.addr[3];
-}
-
-/*
-**  BAR0 - SDRAM, MP_MEMORY_SIZE, MP2_MEMORY_SIZE by Rev.2
-**  BAR1 - DEVICES,                            0x1000
-**  BAR2 - CONTROL (REG), 0x2000
-**  BAR3 - FLASH (REG),                0x8000
-**  BAR4 - CONFIG (CFG),       0x1000
-*/
-int diva_pri_init_card(diva_os_xdi_adapter_t *a)
-{
-       int bar = 0;
-       int pri_rev_2;
-       unsigned long bar_length[5] = {
-               MP_MEMORY_SIZE,
-               0x1000,
-               0x2000,
-               0x8000,
-               0x1000
-       };
-
-       pri_rev_2 = pri_is_rev_2_card(a->CardOrdinal);
-
-       if (pri_rev_2) {
-               bar_length[0] = MP2_MEMORY_SIZE;
-       }
-       /*
-         Set properties
-       */
-       a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
-       DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
-
-               /*
-                 First initialization step: get and check hardware resoures.
-                 Do not map resources and do not acecess card at this step
-               */
-               for (bar = 0; bar < 5; bar++) {
-                       a->resources.pci.bar[bar] =
-                               divasa_get_pci_bar(a->resources.pci.bus,
-                                                  a->resources.pci.func, bar,
-                                                  a->resources.pci.hdev);
-                       if (!a->resources.pci.bar[bar]
-                           || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
-                               DBG_ERR(("A: invalid bar[%d]=%08x", bar,
-                                        a->resources.pci.bar[bar]))
-                                       return (-1);
-                       }
-               }
-       a->resources.pci.irq =
-               (byte) divasa_get_pci_irq(a->resources.pci.bus,
-                                         a->resources.pci.func,
-                                         a->resources.pci.hdev);
-       if (!a->resources.pci.irq) {
-               DBG_ERR(("A: invalid irq"));
-               return (-1);
-       }
-
-       /*
-         Map all BAR's
-       */
-       for (bar = 0; bar < 5; bar++) {
-               a->resources.pci.addr[bar] =
-                       divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
-                                            bar_length[bar]);
-               if (!a->resources.pci.addr[bar]) {
-                       DBG_ERR(("A: A(%d), can't map bar[%d]",
-                                a->controller, bar))
-                               diva_pri_cleanup_adapter(a);
-                       return (-1);
-               }
-       }
-
-       /*
-         Set all memory areas
-       */
-       diva_pri_set_addresses(a);
-
-       /*
-         Get Serial Number of this adapter
-       */
-       if (pri_get_serial_number(a)) {
-               dword serNo;
-               serNo = a->resources.pci.bar[1] & 0xffff0000;
-               serNo |= ((dword) a->resources.pci.bus) << 8;
-               serNo += (a->resources.pci.func + a->controller + 1);
-               a->xdi_adapter.serialNo = serNo & ~0xFF000000;
-               DBG_ERR(("A: A(%d) can't get Serial Number, generated serNo=%ld",
-                        a->controller, a->xdi_adapter.serialNo))
-                       }
-
-
-       /*
-         Initialize os objects
-       */
-       if (diva_os_initialize_spin_lock(&a->xdi_adapter.isr_spin_lock, "isr")) {
-               diva_pri_cleanup_adapter(a);
-               return (-1);
-       }
-       if (diva_os_initialize_spin_lock
-           (&a->xdi_adapter.data_spin_lock, "data")) {
-               diva_pri_cleanup_adapter(a);
-               return (-1);
-       }
-
-       strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasprid");
-
-       if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
-                                       DIDpcRoutine, &a->xdi_adapter)) {
-               diva_pri_cleanup_adapter(a);
-               return (-1);
-       }
-
-       /*
-         Do not initialize second DPC - only one thread will be created
-       */
-       a->xdi_adapter.isr_soft_isr.object =
-               a->xdi_adapter.req_soft_isr.object;
-
-       /*
-         Next step of card initialization:
-         set up all interface pointers
-       */
-       a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
-       a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
-
-       a->xdi_adapter.e_tbl =
-               diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
-       if (!a->xdi_adapter.e_tbl) {
-               diva_pri_cleanup_adapter(a);
-               return (-1);
-       }
-       memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
-
-       a->xdi_adapter.a.io = &a->xdi_adapter;
-       a->xdi_adapter.DIRequest = request;
-       a->interface.cleanup_adapter_proc = diva_pri_cleanup_adapter;
-       a->interface.cmd_proc = diva_pri_cmd_card_proc;
-
-       if (pri_rev_2) {
-               prepare_pri2_functions(&a->xdi_adapter);
-       } else {
-               prepare_pri_functions(&a->xdi_adapter);
-       }
-
-       a->dsp_mask = diva_pri_detect_dsps(a);
-
-       /*
-         Allocate DMA map
-       */
-       if (pri_rev_2) {
-               diva_init_dma_map(a->resources.pci.hdev,
-                                 (struct _diva_dma_map_entry **) &a->xdi_adapter.dma_map, 32);
-       }
-
-       /*
-         Set IRQ handler
-       */
-       a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
-       sprintf(a->xdi_adapter.irq_info.irq_name,
-               "DIVA PRI %ld", (long) a->xdi_adapter.serialNo);
-
-       if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
-                                a->xdi_adapter.irq_info.irq_name)) {
-               diva_pri_cleanup_adapter(a);
-               return (-1);
-       }
-       a->xdi_adapter.irq_info.registered = 1;
-
-       diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
-                     a->resources.pci.irq, a->xdi_adapter.serialNo);
-
-       return (0);
-}
-
-static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t *a)
-{
-       int bar = 0;
-
-       /*
-         Stop Adapter if adapter is running
-       */
-       if (a->xdi_adapter.Initialized) {
-               diva_pri_stop_adapter(a);
-       }
-
-       /*
-         Remove ISR Handler
-       */
-       if (a->xdi_adapter.irq_info.registered) {
-               diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
-       }
-       a->xdi_adapter.irq_info.registered = 0;
-
-       /*
-         Step 1: unmap all BAR's, if any was mapped
-       */
-       for (bar = 0; bar < 5; bar++) {
-               if (a->resources.pci.bar[bar]
-                   && a->resources.pci.addr[bar]) {
-                       divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
-                       a->resources.pci.bar[bar] = 0;
-                       a->resources.pci.addr[bar] = NULL;
-               }
-       }
-
-       /*
-         Free OS objects
-       */
-       diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
-       diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
-
-       diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
-       a->xdi_adapter.isr_soft_isr.object = NULL;
-
-       diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
-       diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
-
-       /*
-         Free memory accupied by XDI adapter
-       */
-       if (a->xdi_adapter.e_tbl) {
-               diva_os_free(0, a->xdi_adapter.e_tbl);
-               a->xdi_adapter.e_tbl = NULL;
-       }
-       a->xdi_adapter.Channels = 0;
-       a->xdi_adapter.e_max = 0;
-
-
-       /*
-         Free adapter DMA map
-       */
-       diva_free_dma_map(a->resources.pci.hdev,
-                         (struct _diva_dma_map_entry *) a->xdi_adapter.
-                         dma_map);
-       a->xdi_adapter.dma_map = NULL;
-
-
-       /*
-         Detach this adapter from debug driver
-       */
-
-       return (0);
-}
-
-/*
-**  Activate On Board Boot Loader
-*/
-static int diva_pri_reset_adapter(PISDN_ADAPTER IoAdapter)
-{
-       dword i;
-       struct mp_load __iomem *boot;
-
-       if (!IoAdapter->Address || !IoAdapter->reset) {
-               return (-1);
-       }
-       if (IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't reset PRI adapter - please stop first",
-                        IoAdapter->ANum))
-                       return (-1);
-       }
-
-       boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       WRITE_DWORD(&boot->err, 0);
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-
-       IoAdapter->rstFnc(IoAdapter);
-
-       diva_os_wait(10);
-
-       boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       i = READ_DWORD(&boot->live);
-
-       diva_os_wait(10);
-       if (i == READ_DWORD(&boot->live)) {
-               DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-               DBG_ERR(("A: A(%d) CPU on PRI %ld is not alive!",
-                        IoAdapter->ANum, IoAdapter->serialNo))
-                       return (-1);
-       }
-       if (READ_DWORD(&boot->err)) {
-               DBG_ERR(("A: A(%d) PRI %ld Board Selftest failed, error=%08lx",
-                        IoAdapter->ANum, IoAdapter->serialNo,
-                        READ_DWORD(&boot->err)))
-                       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-               return (-1);
-       }
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-
-       /*
-         Forget all outstanding entities
-       */
-       IoAdapter->e_count = 0;
-       if (IoAdapter->e_tbl) {
-               memset(IoAdapter->e_tbl, 0x00,
-                      IoAdapter->e_max * sizeof(E_INFO));
-       }
-       IoAdapter->head = 0;
-       IoAdapter->tail = 0;
-       IoAdapter->assign = 0;
-       IoAdapter->trapped = 0;
-
-       memset(&IoAdapter->a.IdTable[0], 0x00,
-              sizeof(IoAdapter->a.IdTable));
-       memset(&IoAdapter->a.IdTypeTable[0], 0x00,
-              sizeof(IoAdapter->a.IdTypeTable));
-       memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
-              sizeof(IoAdapter->a.FlowControlIdTable));
-       memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
-              sizeof(IoAdapter->a.FlowControlSkipTable));
-       memset(&IoAdapter->a.misc_flags_table[0], 0x00,
-              sizeof(IoAdapter->a.misc_flags_table));
-       memset(&IoAdapter->a.rx_stream[0], 0x00,
-              sizeof(IoAdapter->a.rx_stream));
-       memset(&IoAdapter->a.tx_stream[0], 0x00,
-              sizeof(IoAdapter->a.tx_stream));
-       memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
-       memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
-
-       return (0);
-}
-
-static int
-diva_pri_write_sdram_block(PISDN_ADAPTER IoAdapter,
-                          dword address,
-                          const byte *data, dword length, dword limit)
-{
-       byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       byte __iomem *mem = p;
-
-       if (((address + length) >= limit) || !mem) {
-               DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
-               DBG_ERR(("A: A(%d) write PRI address=0x%08lx",
-                        IoAdapter->ANum, address + length))
-                       return (-1);
-       }
-       mem += address;
-
-       /* memcpy_toio(), maybe? */
-       while (length--) {
-               WRITE_BYTE(mem++, *data++);
-       }
-
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
-       return (0);
-}
-
-static int
-diva_pri_start_adapter(PISDN_ADAPTER IoAdapter,
-                      dword start_address, dword features)
-{
-       dword i;
-       int started = 0;
-       byte __iomem *p;
-       struct mp_load __iomem *boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       ADAPTER *a = &IoAdapter->a;
-
-       if (IoAdapter->Initialized) {
-               DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-               DBG_ERR(("A: A(%d) pri_start_adapter, adapter already running",
-                        IoAdapter->ANum))
-                       return (-1);
-       }
-       if (!boot) {
-               DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-               DBG_ERR(("A: PRI %ld can't start, adapter not mapped",
-                        IoAdapter->serialNo))
-                       return (-1);
-       }
-
-       sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
-       DBG_LOG(("A(%d) start PRI at 0x%08lx", IoAdapter->ANum,
-                start_address))
-
-               WRITE_DWORD(&boot->addr, start_address);
-       WRITE_DWORD(&boot->cmd, 3);
-
-       for (i = 0; i < 300; ++i) {
-               diva_os_wait(10);
-               if ((READ_DWORD(&boot->signature) >> 16) == 0x4447) {
-                       DBG_LOG(("A(%d) Protocol startup time %d.%02d seconds",
-                                IoAdapter->ANum, (i / 100), (i % 100)))
-                               started = 1;
-                       break;
-               }
-       }
-
-       if (!started) {
-               byte __iomem *p = (byte __iomem *)boot;
-               dword TrapId;
-               dword debug;
-               TrapId = READ_DWORD(&p[0x80]);
-               debug = READ_DWORD(&p[0x1c]);
-               DBG_ERR(("A(%d) Adapter start failed 0x%08lx, TrapId=%08lx, debug=%08lx",
-                        IoAdapter->ANum, READ_DWORD(&boot->signature),
-                        TrapId, debug))
-                       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-               if (IoAdapter->trapFnc) {
-                       (*(IoAdapter->trapFnc)) (IoAdapter);
-               }
-               IoAdapter->stop(IoAdapter);
-               return (-1);
-       }
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
-
-       IoAdapter->Initialized = true;
-
-       /*
-         Check Interrupt
-       */
-       IoAdapter->IrqCount = 0;
-       p = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
-       WRITE_DWORD(p, (dword)~0x03E00000);
-       DIVA_OS_MEM_DETACH_CFG(IoAdapter, p);
-       a->ReadyInt = 1;
-       a->ram_out(a, &PR_RAM->ReadyInt, 1);
-
-       for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
-
-       if (!IoAdapter->IrqCount) {
-               DBG_ERR(("A: A(%d) interrupt test failed",
-                        IoAdapter->ANum))
-                       IoAdapter->Initialized = false;
-               IoAdapter->stop(IoAdapter);
-               return (-1);
-       }
-
-       IoAdapter->Properties.Features = (word) features;
-
-       diva_xdi_display_adapter_features(IoAdapter->ANum);
-
-       DBG_LOG(("A(%d) PRI adapter successfully started", IoAdapter->ANum))
-               /*
-                 Register with DIDD
-               */
-               diva_xdi_didd_register_adapter(IoAdapter->ANum);
-
-       return (0);
-}
-
-static void diva_pri_clear_interrupts(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-
-       /*
-         clear any pending interrupt
-       */
-       IoAdapter->disIrq(IoAdapter);
-
-       IoAdapter->tst_irq(&IoAdapter->a);
-       IoAdapter->clr_irq(&IoAdapter->a);
-       IoAdapter->tst_irq(&IoAdapter->a);
-
-       /*
-         kill pending dpcs
-       */
-       diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
-       diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
-}
-
-/*
-**  Stop Adapter, but do not unmap/unregister - adapter
-**  will be restarted later
-*/
-static int diva_pri_stop_adapter(diva_os_xdi_adapter_t *a)
-{
-       PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
-       int i = 100;
-
-       if (!IoAdapter->ram) {
-               return (-1);
-       }
-       if (!IoAdapter->Initialized) {
-               DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
-                        IoAdapter->ANum))
-                       return (-1);    /* nothing to stop */
-       }
-       IoAdapter->Initialized = 0;
-
-       /*
-         Disconnect Adapter from DIDD
-       */
-       diva_xdi_didd_remove_adapter(IoAdapter->ANum);
-
-       /*
-         Stop interrupts
-       */
-       a->clear_interrupts_proc = diva_pri_clear_interrupts;
-       IoAdapter->a.ReadyInt = 1;
-       IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
-       do {
-               diva_os_sleep(10);
-       } while (i-- && a->clear_interrupts_proc);
-
-       if (a->clear_interrupts_proc) {
-               diva_pri_clear_interrupts(a);
-               a->clear_interrupts_proc = NULL;
-               DBG_ERR(("A: A(%d) no final interrupt from PRI adapter",
-                        IoAdapter->ANum))
-                       }
-       IoAdapter->a.ReadyInt = 0;
-
-       /*
-         Stop and reset adapter
-       */
-       IoAdapter->stop(IoAdapter);
-
-       return (0);
-}
-
-/*
-**  Process commands form configuration/download framework and from
-**  user mode
-**
-**  return 0 on success
-*/
-static int
-diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
-                      diva_xdi_um_cfg_cmd_t *cmd, int length)
-{
-       int ret = -1;
-
-       if (cmd->adapter != a->controller) {
-               DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
-                        cmd->adapter, a->controller))
-                       return (-1);
-       }
-
-       switch (cmd->command) {
-       case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->CardOrdinal;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       *(dword *) a->xdi_mbox.data =
-                               (dword) a->xdi_adapter.serialNo;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
-               a->xdi_mbox.data_length = sizeof(dword) * 9;
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       int i;
-                       dword *data = (dword *) a->xdi_mbox.data;
-
-                       for (i = 0; i < 8; i++) {
-                               *data++ = a->resources.pci.bar[i];
-                       }
-                       *data++ = (dword) a->resources.pci.irq;
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_RESET_ADAPTER:
-               ret = diva_pri_reset_adapter(&a->xdi_adapter);
-               break;
-
-       case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
-               ret = diva_pri_write_sdram_block(&a->xdi_adapter,
-                                                cmd->command_data.
-                                                write_sdram.offset,
-                                                (byte *)&cmd[1],
-                                                cmd->command_data.
-                                                write_sdram.length,
-                                                pri_is_rev_2_card(a->
-                                                                  CardOrdinal)
-                                                ? MP2_MEMORY_SIZE :
-                                                MP_MEMORY_SIZE);
-               break;
-
-       case DIVA_XDI_UM_CMD_STOP_ADAPTER:
-               ret = diva_pri_stop_adapter(a);
-               break;
-
-       case DIVA_XDI_UM_CMD_START_ADAPTER:
-               ret = diva_pri_start_adapter(&a->xdi_adapter,
-                                            cmd->command_data.start.
-                                            offset,
-                                            cmd->command_data.start.
-                                            features);
-               break;
-
-       case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
-               a->xdi_adapter.features =
-                       cmd->command_data.features.features;
-               a->xdi_adapter.a.protocol_capabilities =
-                       a->xdi_adapter.features;
-               DBG_TRC(("Set raw protocol features (%08x)",
-                        a->xdi_adapter.features))
-                       ret = 0;
-               break;
-
-       case DIVA_XDI_UM_CMD_GET_CARD_STATE:
-               a->xdi_mbox.data_length = sizeof(dword);
-               a->xdi_mbox.data =
-                       diva_os_malloc(0, a->xdi_mbox.data_length);
-               if (a->xdi_mbox.data) {
-                       dword *data = (dword *) a->xdi_mbox.data;
-                       if (!a->xdi_adapter.ram ||
-                           !a->xdi_adapter.reset ||
-                           !a->xdi_adapter.cfg) {
-                               *data = 3;
-                       } else if (a->xdi_adapter.trapped) {
-                               *data = 2;
-                       } else if (a->xdi_adapter.Initialized) {
-                               *data = 1;
-                       } else {
-                               *data = 0;
-                       }
-                       a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                       ret = 0;
-               }
-               break;
-
-       case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
-               ret = diva_card_read_xlog(a);
-               break;
-
-       case DIVA_XDI_UM_CMD_READ_SDRAM:
-               if (a->xdi_adapter.Address) {
-                       if (
-                               (a->xdi_mbox.data_length =
-                                cmd->command_data.read_sdram.length)) {
-                               if (
-                                       (a->xdi_mbox.data_length +
-                                        cmd->command_data.read_sdram.offset) <
-                                       a->xdi_adapter.MemorySize) {
-                                       a->xdi_mbox.data =
-                                               diva_os_malloc(0,
-                                                              a->xdi_mbox.
-                                                              data_length);
-                                       if (a->xdi_mbox.data) {
-                                               byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
-                                               byte __iomem *src = p;
-                                               byte *dst = a->xdi_mbox.data;
-                                               dword len = a->xdi_mbox.data_length;
-
-                                               src += cmd->command_data.read_sdram.offset;
-
-                                               while (len--) {
-                                                       *dst++ = READ_BYTE(src++);
-                                               }
-                                               a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
-                                               DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
-                                               ret = 0;
-                                       }
-                               }
-                       }
-               }
-               break;
-
-       default:
-               DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
-                        cmd->command))
-                       }
-
-       return (ret);
-}
-
-/*
-**  Get Serial Number
-*/
-static int pri_get_serial_number(diva_os_xdi_adapter_t *a)
-{
-       byte data[64];
-       int i;
-       dword len = sizeof(data);
-       volatile byte __iomem *config;
-       volatile byte __iomem *flash;
-       byte c;
-
-/*
- *  First set some GT6401x config registers before accessing the BOOT-ROM
- */
-       config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
-       c = READ_BYTE(&config[0xc3c]);
-       if (!(c & 0x08)) {
-               WRITE_BYTE(&config[0xc3c], c);  /* Base Address enable register */
-       }
-       WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0x00);
-       WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
-       DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
-/*
- *  Read only the last 64 bytes of manufacturing data
- */
-       memset(data, '\0', len);
-       flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
-       for (i = 0; i < len; i++) {
-               data[i] = READ_BYTE(&flash[0x8000 - len + i]);
-       }
-       DIVA_OS_MEM_DETACH_PROM(&a->xdi_adapter, flash);
-
-       config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
-       WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0xFC);     /* Disable FLASH EPROM access */
-       WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
-       DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
-
-       if (memcmp(&data[48], "DIVAserverPR", 12)) {
-#if !defined(DIVA_PRI_NO_PCI_BIOS_WORKAROUND)  /* { */
-               word cmd = 0, cmd_org;
-               void *addr;
-               dword addr1, addr3, addr4;
-               byte Bus, Slot;
-               void *hdev;
-               addr4 = a->resources.pci.bar[4];
-               addr3 = a->resources.pci.bar[3];        /* flash  */
-               addr1 = a->resources.pci.bar[1];        /* unused */
-
-               DBG_ERR(("A: apply Compaq BIOS workaround"))
-                       DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                                data[0], data[1], data[2], data[3],
-                                data[4], data[5], data[6], data[7]))
-
-                       Bus = a->resources.pci.bus;
-               Slot = a->resources.pci.func;
-               hdev = a->resources.pci.hdev;
-               PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
-               PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
-
-               PCIwrite(Bus, Slot, 0x14, &addr4, sizeof(addr4), hdev);
-               PCIwrite(Bus, Slot, 0x20, &addr1, sizeof(addr1), hdev);
-
-               PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
-
-               addr = a->resources.pci.addr[1];
-               a->resources.pci.addr[1] = a->resources.pci.addr[4];
-               a->resources.pci.addr[4] = addr;
-
-               addr1 = a->resources.pci.bar[1];
-               a->resources.pci.bar[1] = a->resources.pci.bar[4];
-               a->resources.pci.bar[4] = addr1;
-
-               /*
-                 Try to read Flash again
-               */
-               len = sizeof(data);
-
-               config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
-               if (!(config[0xc3c] & 0x08)) {
-                       config[0xc3c] |= 0x08;  /* Base Address enable register */
-               }
-               config[LOW_BOOTCS_DREG] = 0x00;
-               config[HI_BOOTCS_DREG] = 0xFF;
-               DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
-
-               memset(data, '\0', len);
-               flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
-               for (i = 0; i < len; i++) {
-                       data[i] = flash[0x8000 - len + i];
-               }
-               DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter, flash);
-               config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
-               config[LOW_BOOTCS_DREG] = 0xFC;
-               config[HI_BOOTCS_DREG] = 0xFF;
-               DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
-
-               if (memcmp(&data[48], "DIVAserverPR", 12)) {
-                       DBG_ERR(("A: failed to read serial number"))
-                               DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                                        data[0], data[1], data[2], data[3],
-                                        data[4], data[5], data[6], data[7]))
-                               return (-1);
-               }
-#else                          /* } { */
-               DBG_ERR(("A: failed to read DIVA signature word"))
-                       DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
-                                data[0], data[1], data[2], data[3],
-                                data[4], data[5], data[6], data[7]))
-                       DBG_LOG(("%02x:%02x:%02x:%02x", data[47], data[46],
-                                data[45], data[44]))
-#endif                         /* } */
-                       }
-
-       a->xdi_adapter.serialNo =
-               (data[47] << 24) | (data[46] << 16) | (data[45] << 8) |
-               data[44];
-       if (!a->xdi_adapter.serialNo
-           || (a->xdi_adapter.serialNo == 0xffffffff)) {
-               a->xdi_adapter.serialNo = 0;
-               DBG_ERR(("A: failed to read serial number"))
-                       return (-1);
-       }
-
-       DBG_LOG(("Serial No.          : %ld", a->xdi_adapter.serialNo))
-               DBG_TRC(("Board Revision      : %d.%02d", (int) data[41],
-                        (int) data[40]))
-               DBG_TRC(("PLD revision        : %d.%02d", (int) data[33],
-                        (int) data[32]))
-               DBG_TRC(("Boot loader version : %d.%02d", (int) data[37],
-                        (int) data[36]))
-
-               DBG_TRC(("Manufacturing Date  : %d/%02d/%02d  (yyyy/mm/dd)",
-                        (int) ((data[28] > 90) ? 1900 : 2000) +
-                        (int) data[28], (int) data[29], (int) data[30]))
-
-               return (0);
-}
-
-void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter)
-{
-}
-
-void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter)
-{
-}
-
-/*
-**  Checks presence of DSP on board
-*/
-static int
-dsp_check_presence(volatile byte __iomem *addr, volatile byte __iomem *data, int dsp)
-{
-       word pattern;
-
-       WRITE_WORD(addr, 0x4000);
-       WRITE_WORD(data, DSP_SIGNATURE_PROBE_WORD);
-
-       WRITE_WORD(addr, 0x4000);
-       pattern = READ_WORD(data);
-
-       if (pattern != DSP_SIGNATURE_PROBE_WORD) {
-               DBG_TRC(("W: DSP[%d] %04x(is) != %04x(should)",
-                        dsp, pattern, DSP_SIGNATURE_PROBE_WORD))
-                       return (-1);
-       }
-
-       WRITE_WORD(addr, 0x4000);
-       WRITE_WORD(data, ~DSP_SIGNATURE_PROBE_WORD);
-
-       WRITE_WORD(addr, 0x4000);
-       pattern = READ_WORD(data);
-
-       if (pattern != (word)~DSP_SIGNATURE_PROBE_WORD) {
-               DBG_ERR(("A: DSP[%d] %04x(is) != %04x(should)",
-                        dsp, pattern, (word)~DSP_SIGNATURE_PROBE_WORD))
-                       return (-2);
-       }
-
-       DBG_TRC(("DSP[%d] present", dsp))
-
-               return (0);
-}
-
-
-/*
-**  Check if DSP's are present and operating
-**  Information about detected DSP's is returned as bit mask
-**  Bit 0  - DSP1
-**  ...
-**  ...
-**  ...
-**  Bit 29 - DSP30
-*/
-static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t *a)
-{
-       byte __iomem *base;
-       byte __iomem *p;
-       dword ret = 0;
-       dword row_offset[7] = {
-               0x00000000,
-               0x00000800,     /* 1 - ROW 1 */
-               0x00000840,     /* 2 - ROW 2 */
-               0x00001000,     /* 3 - ROW 3 */
-               0x00001040,     /* 4 - ROW 4 */
-               0x00000000      /* 5 - ROW 0 */
-       };
-
-       byte __iomem *dsp_addr_port;
-       byte __iomem *dsp_data_port;
-       byte row_state;
-       int dsp_row = 0, dsp_index, dsp_num;
-
-       if (!a->xdi_adapter.Control || !a->xdi_adapter.reset) {
-               return (0);
-       }
-
-       p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
-       WRITE_BYTE(p, _MP_RISC_RESET | _MP_DSP_RESET);
-       DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
-       diva_os_wait(5);
-
-       base = DIVA_OS_MEM_ATTACH_CONTROL(&a->xdi_adapter);
-
-       for (dsp_num = 0; dsp_num < 30; dsp_num++) {
-               dsp_row = dsp_num / 7 + 1;
-               dsp_index = dsp_num % 7;
-
-               dsp_data_port = base;
-               dsp_addr_port = base;
-
-               dsp_data_port += row_offset[dsp_row];
-               dsp_addr_port += row_offset[dsp_row];
-
-               dsp_data_port += (dsp_index * 8);
-               dsp_addr_port += (dsp_index * 8) + 0x80;
-
-               if (!dsp_check_presence
-                   (dsp_addr_port, dsp_data_port, dsp_num + 1)) {
-                       ret |= (1 << dsp_num);
-               }
-       }
-       DIVA_OS_MEM_DETACH_CONTROL(&a->xdi_adapter, base);
-
-       p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
-       WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
-       DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
-       diva_os_wait(5);
-
-       /*
-         Verify modules
-       */
-       for (dsp_row = 0; dsp_row < 4; dsp_row++) {
-               row_state = ((ret >> (dsp_row * 7)) & 0x7F);
-               if (row_state && (row_state != 0x7F)) {
-                       for (dsp_index = 0; dsp_index < 7; dsp_index++) {
-                               if (!(row_state & (1 << dsp_index))) {
-                                       DBG_ERR(("A: MODULE[%d]-DSP[%d] failed",
-                                                dsp_row + 1,
-                                                dsp_index + 1))
-                                               }
-                       }
-               }
-       }
-
-       if (!(ret & 0x10000000)) {
-               DBG_ERR(("A: ON BOARD-DSP[1] failed"))
-                       }
-       if (!(ret & 0x20000000)) {
-               DBG_ERR(("A: ON BOARD-DSP[2] failed"))
-                       }
-
-       /*
-         Print module population now
-       */
-       DBG_LOG(("+-----------------------+"))
-               DBG_LOG(("| DSP MODULE POPULATION |"))
-               DBG_LOG(("+-----------------------+"))
-               DBG_LOG(("|  1  |  2  |  3  |  4  |"))
-               DBG_LOG(("+-----------------------+"))
-               DBG_LOG(("|  %s  |  %s  |  %s  |  %s  |",
-                        ((ret >> (0 * 7)) & 0x7F) ? "Y" : "N",
-                        ((ret >> (1 * 7)) & 0x7F) ? "Y" : "N",
-                        ((ret >> (2 * 7)) & 0x7F) ? "Y" : "N",
-                        ((ret >> (3 * 7)) & 0x7F) ? "Y" : "N"))
-               DBG_LOG(("+-----------------------+"))
-
-               DBG_LOG(("DSP's(present-absent):%08x-%08x", ret,
-                        ~ret & 0x3fffffff))
-
-               return (ret);
-}
diff --git a/drivers/isdn/hardware/eicon/os_pri.h b/drivers/isdn/hardware/eicon/os_pri.h
deleted file mode 100644 (file)
index 0e91855..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: os_pri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
-
-#ifndef __DIVA_OS_PRI_REV_1_H__
-#define __DIVA_OS_PRI_REV_1_H__
-
-int diva_pri_init_card(diva_os_xdi_adapter_t *a);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/pc.h b/drivers/isdn/hardware/eicon/pc.h
deleted file mode 100644 (file)
index 329c0c2..0000000
+++ /dev/null
@@ -1,738 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef PC_H_INCLUDED  /* { */
-#define PC_H_INCLUDED
-/*------------------------------------------------------------------*/
-/* buffer definition                                                */
-/*------------------------------------------------------------------*/
-typedef struct {
-       word length;          /* length of data/parameter field           */
-       byte P[270];          /* data/parameter field                     */
-} PBUFFER;
-/*------------------------------------------------------------------*/
-/* dual port ram structure                                          */
-/*------------------------------------------------------------------*/
-struct dual
-{
-       byte Req;             /* request register                         */
-       byte ReqId;           /* request task/entity identification       */
-       byte Rc;              /* return code register                     */
-       byte RcId;            /* return code task/entity identification   */
-       byte Ind;             /* Indication register                      */
-       byte IndId;           /* Indication task/entity identification    */
-       byte IMask;           /* Interrupt Mask Flag                      */
-       byte RNR;             /* Receiver Not Ready (set by PC)           */
-       byte XLock;           /* XBuffer locked Flag                      */
-       byte Int;             /* ISDN-S interrupt                         */
-       byte ReqCh;           /* Channel field for layer-3 Requests       */
-       byte RcCh;            /* Channel field for layer-3 Returncodes    */
-       byte IndCh;           /* Channel field for layer-3 Indications    */
-       byte MInd;            /* more data indication field               */
-       word MLength;         /* more data total packet length            */
-       byte ReadyInt;        /* request field for ready interrupt        */
-       byte SWReg;           /* Software register for special purposes   */
-       byte Reserved[11];    /* reserved space                           */
-       byte InterfaceType;   /* interface type 1=16K interface           */
-       word Signature;       /* ISDN-S adapter Signature (GD)            */
-       PBUFFER XBuffer;      /* Transmit Buffer                          */
-       PBUFFER RBuffer;      /* Receive Buffer                           */
-};
-/*------------------------------------------------------------------*/
-/* SWReg Values (0 means no command)                                */
-/*------------------------------------------------------------------*/
-#define SWREG_DIE_WITH_LEDON  0x01
-#define SWREG_HALT_CPU        0x02 /* Push CPU into a while (1) loop */
-/*------------------------------------------------------------------*/
-/* Id Fields Coding                                                 */
-/*------------------------------------------------------------------*/
-#define ID_MASK 0xe0    /* Mask for the ID field                    */
-#define GL_ERR_ID 0x1f  /* ID for error reporting on global requests*/
-#define DSIG_ID  0x00   /* ID for D-channel signaling               */
-#define NL_ID    0x20   /* ID for network-layer access (B or D)     */
-#define BLLC_ID  0x60   /* ID for B-channel link level access       */
-#define TASK_ID  0x80   /* ID for dynamic user tasks                */
-#define TIMER_ID 0xa0   /* ID for timer task                        */
-#define TEL_ID   0xc0   /* ID for telephone support                 */
-#define MAN_ID   0xe0   /* ID for management                        */
-/*------------------------------------------------------------------*/
-/* ASSIGN and REMOVE requests are the same for all entities         */
-/*------------------------------------------------------------------*/
-#define ASSIGN  0x01
-#define UREMOVE 0xfe /* without return code */
-#define REMOVE  0xff
-/*------------------------------------------------------------------*/
-/* Timer Interrupt Task Interface                                   */
-/*------------------------------------------------------------------*/
-#define ASSIGN_TIM 0x01
-#define REMOVE_TIM 0xff
-/*------------------------------------------------------------------*/
-/* dynamic user task interface                                      */
-/*------------------------------------------------------------------*/
-#define ASSIGN_TSK 0x01
-#define REMOVE_TSK 0xff
-#define LOAD 0xf0
-#define RELOCATE 0xf1
-#define START 0xf2
-#define LOAD2 0xf3
-#define RELOCATE2 0xf4
-/*------------------------------------------------------------------*/
-/* dynamic user task messages                                       */
-/*------------------------------------------------------------------*/
-#define TSK_B2          0x0000
-#define TSK_WAKEUP      0x2000
-#define TSK_TIMER       0x4000
-#define TSK_TSK         0x6000
-#define TSK_PC          0xe000
-/*------------------------------------------------------------------*/
-/* LL management primitives                                         */
-/*------------------------------------------------------------------*/
-#define ASSIGN_LL 1     /* assign logical link                      */
-#define REMOVE_LL 0xff  /* remove logical link                      */
-/*------------------------------------------------------------------*/
-/* LL service primitives                                            */
-/*------------------------------------------------------------------*/
-#define LL_UDATA 1      /* link unit data request/indication        */
-#define LL_ESTABLISH 2  /* link establish request/indication        */
-#define LL_RELEASE 3    /* link release request/indication          */
-#define LL_DATA 4       /* data request/indication                  */
-#define LL_LOCAL 5      /* switch to local operation (COM only)     */
-#define LL_DATA_PEND 5  /* data pending indication (SDLC SHM only)  */
-#define LL_REMOTE 6     /* switch to remote operation (COM only)    */
-#define LL_TEST 8       /* link test request                        */
-#define LL_MDATA 9      /* more data request/indication             */
-#define LL_BUDATA 10    /* broadcast unit data request/indication   */
-#define LL_XID 12       /* XID command request/indication           */
-#define LL_XID_R 13     /* XID response request/indication          */
-/*------------------------------------------------------------------*/
-/* NL service primitives                                            */
-/*------------------------------------------------------------------*/
-#define N_MDATA         1       /* more data to come REQ/IND        */
-#define N_CONNECT       2       /* OSI N-CONNECT REQ/IND            */
-#define N_CONNECT_ACK   3       /* OSI N-CONNECT CON/RES            */
-#define N_DISC          4       /* OSI N-DISC REQ/IND               */
-#define N_DISC_ACK      5       /* OSI N-DISC CON/RES               */
-#define N_RESET         6       /* OSI N-RESET REQ/IND              */
-#define N_RESET_ACK     7       /* OSI N-RESET CON/RES              */
-#define N_DATA          8       /* OSI N-DATA REQ/IND               */
-#define N_EDATA         9       /* OSI N-EXPEDITED DATA REQ/IND     */
-#define N_UDATA         10      /* OSI D-UNIT-DATA REQ/IND          */
-#define N_BDATA         11      /* BROADCAST-DATA REQ/IND           */
-#define N_DATA_ACK      12      /* data ack ind for D-bit procedure */
-#define N_EDATA_ACK     13      /* data ack ind for INTERRUPT       */
-#define N_XON           15      /* clear RNR state */
-#define N_COMBI_IND     N_XON   /* combined indication              */
-#define N_Q_BIT         0x10    /* Q-bit for req/ind                */
-#define N_M_BIT         0x20    /* M-bit for req/ind                */
-#define N_D_BIT         0x40    /* D-bit for req/ind                */
-/*------------------------------------------------------------------*/
-/* Signaling management primitives                                  */
-/*------------------------------------------------------------------*/
-#define ASSIGN_SIG  1    /* assign signaling task                    */
-#define UREMOVE_SIG 0xfe /* remove signaling task without return code*/
-#define REMOVE_SIG  0xff /* remove signaling task                    */
-/*------------------------------------------------------------------*/
-/* Signaling service primitives                                     */
-/*------------------------------------------------------------------*/
-#define CALL_REQ 1      /* call request                             */
-#define CALL_CON 1      /* call confirmation                        */
-#define CALL_IND 2      /* incoming call connected                  */
-#define LISTEN_REQ 2    /* listen request                           */
-#define HANGUP 3        /* hangup request/indication                */
-#define SUSPEND 4       /* call suspend request/confirm             */
-#define RESUME 5        /* call resume request/confirm              */
-#define SUSPEND_REJ 6   /* suspend rejected indication              */
-#define USER_DATA 8     /* user data for user to user signaling     */
-#define CONGESTION 9    /* network congestion indication            */
-#define INDICATE_REQ 10 /* request to indicate an incoming call     */
-#define INDICATE_IND 10 /* indicates that there is an incoming call */
-#define CALL_RES 11     /* accept an incoming call                  */
-#define CALL_ALERT 12   /* send ALERT for incoming call             */
-#define INFO_REQ 13     /* INFO request                             */
-#define INFO_IND 13     /* INFO indication                          */
-#define REJECT 14       /* reject an incoming call                  */
-#define RESOURCES 15    /* reserve B-Channel hardware resources     */
-#define HW_CTRL 16      /* B-Channel hardware IOCTL req/ind         */
-#define TEL_CTRL 16     /* Telephone control request/indication     */
-#define STATUS_REQ 17   /* Request D-State (returned in INFO_IND)   */
-#define FAC_REG_REQ 18  /* 1TR6 connection independent fac reg      */
-#define FAC_REG_ACK 19  /* 1TR6 fac registration acknowledge        */
-#define FAC_REG_REJ 20  /* 1TR6 fac registration reject             */
-#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call       */
-#define SW_CTRL 22      /* extended software features               */
-#define REGISTER_REQ 23 /* Q.931 connection independent reg req     */
-#define REGISTER_IND 24 /* Q.931 connection independent reg ind     */
-#define FACILITY_REQ 25 /* Q.931 connection independent fac req     */
-#define FACILITY_IND 26 /* Q.931 connection independent fac ind     */
-#define NCR_INFO_REQ 27 /* INFO_REQ with NULL CR                    */
-#define GCR_MIM_REQ 28  /* MANAGEMENT_INFO_REQ with global CR       */
-#define SIG_CTRL    29  /* Control for Signalling Hardware          */
-#define DSP_CTRL    30  /* Control for DSPs                         */
-#define LAW_REQ      31 /* Law config request for (returns info_i)  */
-#define SPID_CTRL    32 /* Request/indication SPID related          */
-#define NCR_FACILITY 33 /* Request/indication with NULL/DUMMY CR    */
-#define CALL_HOLD    34 /* Request/indication to hold a CALL        */
-#define CALL_RETRIEVE 35 /* Request/indication to retrieve a CALL   */
-#define CALL_HOLD_ACK 36 /* OK of                hold a CALL        */
-#define CALL_RETRIEVE_ACK 37 /* OK of             retrieve a CALL   */
-#define CALL_HOLD_REJ 38 /* Reject of            hold a CALL        */
-#define CALL_RETRIEVE_REJ 39 /* Reject of         retrieve a call   */
-#define GCR_RESTART   40 /* Send/Receive Restart message            */
-#define S_SERVICE     41 /* Send/Receive Supplementary Service      */
-#define S_SERVICE_REJ 42 /* Reject Supplementary Service indication */
-#define S_SUPPORTED   43 /* Req/Ind to get Supported Services       */
-#define STATUS_ENQ    44 /* Req to send the D-ch request if !state0 */
-#define CALL_GUARD    45 /* Req/Ind to use the FLAGS_CALL_OUTCHECK  */
-#define CALL_GUARD_HP 46 /* Call Guard function to reject a call    */
-#define CALL_GUARD_IF 47 /* Call Guard function, inform the appl    */
-#define SSEXT_REQ     48 /* Supplem.Serv./QSIG specific request     */
-#define SSEXT_IND     49 /* Supplem.Serv./QSIG specific indication  */
-/* reserved commands for the US protocols */
-#define INT_3PTY_NIND 50 /* US       specific indication            */
-#define INT_CF_NIND   51 /* US       specific indication            */
-#define INT_3PTY_DROP 52 /* US       specific indication            */
-#define INT_MOVE_CONF 53 /* US       specific indication            */
-#define INT_MOVE_RC   54 /* US       specific indication            */
-#define INT_MOVE_FLIPPED_CONF 55 /* US specific indication          */
-#define INT_X5NI_OK   56 /* internal transfer OK indication         */
-#define INT_XDMS_START 57 /* internal transfer OK indication        */
-#define INT_XDMS_STOP 58 /* internal transfer finish indication     */
-#define INT_XDMS_STOP2 59 /* internal transfer send FA              */
-#define INT_CUSTCONF_REJ 60 /* internal conference reject           */
-#define INT_CUSTXFER 61 /* internal transfer request                */
-#define INT_CUSTX_NIND 62 /* internal transfer ack                  */
-#define INT_CUSTXREJ_NIND 63 /* internal transfer rej               */
-#define INT_X5NI_CF_XFER  64 /* internal transfer OK indication     */
-#define VSWITCH_REQ 65        /* communication between protocol and */
-#define VSWITCH_IND 66        /* capifunctions for D-CH-switching   */
-#define MWI_POLL 67     /* Message Waiting Status Request fkt */
-#define CALL_PEND_NOTIFY 68 /* notify capi to set new listen        */
-#define DO_NOTHING 69       /* dont do somethin if you get this     */
-#define INT_CT_REJ 70       /* ECT rejected internal command        */
-#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete  */
-#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete  */
-/*------------------------------------------------------------------*/
-/* management service primitives                                    */
-/*------------------------------------------------------------------*/
-#define MAN_READ        2
-#define MAN_WRITE       3
-#define MAN_EXECUTE     4
-#define MAN_EVENT_ON    5
-#define MAN_EVENT_OFF   6
-#define MAN_LOCK        7
-#define MAN_UNLOCK      8
-#define MAN_INFO_IND    2
-#define MAN_EVENT_IND   3
-#define MAN_TRACE_IND   4
-#define MAN_COMBI_IND   9
-#define MAN_ESC         0x80
-/*------------------------------------------------------------------*/
-/* return code coding                                               */
-/*------------------------------------------------------------------*/
-#define UNKNOWN_COMMAND         0x01    /* unknown command          */
-#define WRONG_COMMAND           0x02    /* wrong command            */
-#define WRONG_ID                0x03    /* unknown task/entity id   */
-#define WRONG_CH                0x04    /* wrong task/entity id     */
-#define UNKNOWN_IE              0x05    /* unknown information el.  */
-#define WRONG_IE                0x06    /* wrong information el.    */
-#define OUT_OF_RESOURCES        0x07    /* ISDN-S card out of res.  */
-#define ISDN_GUARD_REJ          0x09    /* ISDN-Guard SuppServ rej  */
-#define N_FLOW_CONTROL          0x10    /* Flow-Control, retry      */
-#define ASSIGN_RC               0xe0    /* ASSIGN acknowledgement   */
-#define ASSIGN_OK               0xef    /* ASSIGN OK                */
-#define OK_FC                   0xfc    /* Flow-Control RC          */
-#define READY_INT               0xfd    /* Ready interrupt          */
-#define TIMER_INT               0xfe    /* timer interrupt          */
-#define OK                      0xff    /* command accepted         */
-/*------------------------------------------------------------------*/
-/* information elements                                             */
-/*------------------------------------------------------------------*/
-#define SHIFT 0x90              /* codeset shift                    */
-#define MORE 0xa0               /* more data                        */
-#define SDNCMPL 0xa1            /* sending complete                 */
-#define CL 0xb0                 /* congestion level                 */
-/* codeset 0                                                */
-#define SMSG 0x00               /* segmented message                */
-#define BC  0x04                /* Bearer Capability                */
-#define CAU 0x08                /* cause                            */
-#define CAD 0x0c                /* Connected address                */
-#define CAI 0x10                /* call identity                    */
-#define CHI 0x18                /* channel identification           */
-#define LLI 0x19                /* logical link id                  */
-#define CHA 0x1a                /* charge advice                    */
-#define FTY 0x1c                /* Facility                         */
-#define DT  0x29                /* ETSI date/time                   */
-#define KEY 0x2c                /* keypad information element       */
-#define UID 0x2d                /* User id information element      */
-#define DSP 0x28                /* display                          */
-#define SIG 0x34                /* signalling hardware control      */
-#define OAD 0x6c                /* origination address              */
-#define OSA 0x6d                /* origination sub-address          */
-#define CPN 0x70                /* called party number              */
-#define DSA 0x71                /* destination sub-address          */
-#define RDX 0x73                /* redirecting number extended      */
-#define RDN 0x74                /* redirecting number               */
-#define RIN 0x76                /* redirection number               */
-#define IUP 0x76                /* VN6 rerouter->PCS (codeset 6)    */
-#define IPU 0x77                /* VN6 PCS->rerouter (codeset 6)    */
-#define RI  0x79                /* restart indicator                */
-#define MIE 0x7a                /* management info element          */
-#define LLC 0x7c                /* low layer compatibility          */
-#define HLC 0x7d                /* high layer compatibility         */
-#define UUI 0x7e                /* user user information            */
-#define ESC 0x7f                /* escape extension                 */
-#define DLC 0x20                /* data link layer configuration    */
-#define NLC 0x21                /* network layer configuration      */
-#define REDIRECT_IE     0x22    /* redirection request/indication data */
-#define REDIRECT_NET_IE 0x23    /* redirection network override data   */
-/* codeset 6                                                */
-#define SIN 0x01                /* service indicator                */
-#define CIF 0x02                /* charging information             */
-#define DATE 0x03               /* date                             */
-#define CPS 0x07                /* called party status              */
-/*------------------------------------------------------------------*/
-/* ESC information elements                                         */
-/*------------------------------------------------------------------*/
-#define MSGTYPEIE        0x7a   /* Messagetype info element         */
-#define CRIE             0x7b   /* INFO info element                */
-#define CODESET6IE       0xec   /* Tunnel for Codeset 6 IEs         */
-#define VSWITCHIE        0xed   /* VSwitch info element             */
-#define SSEXTIE          0xee   /* Supplem. Service info element    */
-#define PROFILEIE        0xef   /* Profile info element             */
-/*------------------------------------------------------------------*/
-/* TEL_CTRL contents                                                */
-/*------------------------------------------------------------------*/
-#define RING_ON         0x01
-#define RING_OFF        0x02
-#define HANDS_FREE_ON   0x03
-#define HANDS_FREE_OFF  0x04
-#define ON_HOOK         0x80
-#define OFF_HOOK        0x90
-/* operation values used by ETSI supplementary services */
-#define THREE_PTY_BEGIN           0x04
-#define THREE_PTY_END             0x05
-#define ECT_EXECUTE               0x06
-#define ACTIVATION_DIVERSION      0x07
-#define DEACTIVATION_DIVERSION    0x08
-#define CALL_DEFLECTION           0x0D
-#define INTERROGATION_DIVERSION   0x0B
-#define INTERROGATION_SERV_USR_NR 0x11
-#define ACTIVATION_MWI            0x20
-#define DEACTIVATION_MWI          0x21
-#define MWI_INDICATION            0x22
-#define MWI_RESPONSE              0x23
-#define CONF_BEGIN                0x28
-#define CONF_ADD                  0x29
-#define CONF_SPLIT                0x2a
-#define CONF_DROP                 0x2b
-#define CONF_ISOLATE              0x2c
-#define CONF_REATTACH             0x2d
-#define CONF_PARTYDISC            0x2e
-#define CCBS_INFO_RETAIN          0x2f
-#define CCBS_ERASECALLLINKAGEID   0x30
-#define CCBS_STOP_ALERTING        0x31
-#define CCBS_REQUEST              0x32
-#define CCBS_DEACTIVATE           0x33
-#define CCBS_INTERROGATE          0x34
-#define CCBS_STATUS               0x35
-#define CCBS_ERASE                0x36
-#define CCBS_B_FREE               0x37
-#define CCNR_INFO_RETAIN          0x38
-#define CCBS_REMOTE_USER_FREE     0x39
-#define CCNR_REQUEST              0x3a
-#define CCNR_INTERROGATE          0x3b
-#define GET_SUPPORTED_SERVICES    0xff
-#define DIVERSION_PROCEDURE_CFU     0x70
-#define DIVERSION_PROCEDURE_CFB     0x71
-#define DIVERSION_PROCEDURE_CFNR    0x72
-#define DIVERSION_DEACTIVATION_CFU  0x80
-#define DIVERSION_DEACTIVATION_CFB  0x81
-#define DIVERSION_DEACTIVATION_CFNR 0x82
-#define DIVERSION_INTERROGATE_NUM   0x11
-#define DIVERSION_INTERROGATE_CFU   0x60
-#define DIVERSION_INTERROGATE_CFB   0x61
-#define DIVERSION_INTERROGATE_CFNR  0x62
-/* Service Masks */
-#define SMASK_HOLD_RETRIEVE        0x00000001
-#define SMASK_TERMINAL_PORTABILITY 0x00000002
-#define SMASK_ECT                  0x00000004
-#define SMASK_3PTY                 0x00000008
-#define SMASK_CALL_FORWARDING      0x00000010
-#define SMASK_CALL_DEFLECTION      0x00000020
-#define SMASK_MCID                 0x00000040
-#define SMASK_CCBS                 0x00000080
-#define SMASK_MWI                  0x00000100
-#define SMASK_CCNR                 0x00000200
-#define SMASK_CONF                 0x00000400
-/* ----------------------------------------------
-   Types of transfers used to transfer the
-   information in the 'struct RC->Reserved2[8]'
-   The information is transferred as 2 dwords
-   (2 4Byte unsigned values)
-   First of them is the transfer type.
-   2^32-1 possible messages are possible in this way.
-   The context of the second one had no meaning
-   ---------------------------------------------- */
-#define DIVA_RC_TYPE_NONE              0x00000000
-#define DIVA_RC_TYPE_REMOVE_COMPLETE   0x00000008
-#define DIVA_RC_TYPE_STREAM_PTR        0x00000009
-#define DIVA_RC_TYPE_CMA_PTR           0x0000000a
-#define DIVA_RC_TYPE_OK_FC             0x0000000b
-#define DIVA_RC_TYPE_RX_DMA            0x0000000c
-/* ------------------------------------------------------
-   IO Control codes for IN BAND SIGNALING
-   ------------------------------------------------------ */
-#define CTRL_L1_SET_SIG_ID        5
-#define CTRL_L1_SET_DAD           6
-#define CTRL_L1_RESOURCES         7
-/* ------------------------------------------------------ */
-/* ------------------------------------------------------
-   Layer 2 types
-   ------------------------------------------------------ */
-#define X75T            1       /* x.75 for ttx                     */
-#define TRF             2       /* transparent with hdlc framing    */
-#define TRF_IN          3       /* transparent with hdlc fr. inc.   */
-#define SDLC            4       /* sdlc, sna layer-2                */
-#define X75             5       /* x.75 for btx                     */
-#define LAPD            6       /* lapd (Q.921)                     */
-#define X25_L2          7       /* x.25 layer-2                     */
-#define V120_L2         8       /* V.120 layer-2 protocol           */
-#define V42_IN          9       /* V.42 layer-2 protocol, incoming */
-#define V42            10       /* V.42 layer-2 protocol            */
-#define MDM_ATP        11       /* AT Parser built in the L2        */
-#define X75_V42BIS     12       /* x.75 with V.42bis                */
-#define RTPL2_IN       13       /* RTP layer-2 protocol, incoming  */
-#define RTPL2          14       /* RTP layer-2 protocol             */
-#define V120_V42BIS    15       /* V.120 asynchronous mode supporting V.42bis compression */
-#define LISTENER       27       /* Layer 2 to listen line */
-#define MTP2           28       /* MTP2 Layer 2 */
-#define PIAFS_CRC      29       /* PIAFS Layer 2 with CRC calculation at L2 */
-/* ------------------------------------------------------
-   PIAFS DLC DEFINITIONS
-   ------------------------------------------------------ */
-#define PIAFS_64K            0x01
-#define PIAFS_VARIABLE_SPEED 0x02
-#define PIAFS_CHINESE_SPEED    0x04
-#define PIAFS_UDATA_ABILITY_ID    0x80
-#define PIAFS_UDATA_ABILITY_DCDON 0x01
-#define PIAFS_UDATA_ABILITY_DDI   0x80
-/*
-  DLC of PIAFS :
-  Byte | 8 7 6 5 4 3 2 1
-  -----+--------------------------------------------------------
-  0 | 0 0 1 0 0 0 0 0  Data Link Configuration
-  1 | X X X X X X X X  Length of IE (at least 15 Bytes)
-  2 | 0 0 0 0 0 0 0 0  max. information field, LOW  byte (not used, fix 73 Bytes)
-  3 | 0 0 0 0 0 0 0 0  max. information field, HIGH byte (not used, fix 73 Bytes)
-  4 | 0 0 0 0 0 0 0 0  address A (not used)
-  5 | 0 0 0 0 0 0 0 0  address B (not used)
-  6 | 0 0 0 0 0 0 0 0  Mode (not used, fix 128)
-  7 | 0 0 0 0 0 0 0 0  Window Size (not used, fix 127)
-  8 | X X X X X X X X  XID Length, Low Byte (at least 7 Bytes)
-  9 | X X X X X X X X  XID Length, High Byte
-  10 | 0 0 0 0 0 C V S  PIAFS Protocol Speed configuration -> Note(1)
-  |                  S = 0 -> Protocol Speed is 32K
-  |                  S = 1 -> Protocol Speed is 64K
-  |                  V = 0 -> Protocol Speed is fixed
-  |                  V = 1 -> Protocol Speed is variable
-  |                  C = 0 -> speed setting according to standard
-  |                  C = 1 -> speed setting for chinese implementation
-  11 | 0 0 0 0 0 0 R T  P0 - V42bis Compression enable/disable, Low Byte
-  |                  T = 0 -> Transmit Direction enable
-  |                  T = 1 -> Transmit Direction disable
-  |                  R = 0 -> Receive  Direction enable
-  |                  R = 1 -> Receive  Direction disable
-  13 | 0 0 0 0 0 0 0 0  P0 - V42bis Compression enable/disable, High Byte
-  14 | X X X X X X X X  P1 - V42bis Dictionary Size, Low Byte
-  15 | X X X X X X X X  P1 - V42bis Dictionary Size, High Byte
-  16 | X X X X X X X X  P2 - V42bis String Length, Low Byte
-  17 | X X X X X X X X  P2 - V42bis String Length, High Byte
-  18 | X X X X X X X X  PIAFS extension length
-  19 | 1 0 0 0 0 0 0 0  PIAFS extension Id (0x80) - UDATA abilities
-  20 | U 0 0 0 0 0 0 D  UDATA abilities -> Note (2)
-  |                  up to now the following Bits are defined:
-  |                  D - signal DCD ON
-  |                  U - use extensive UDATA control communication
-  |                      for DDI test application
-  + Note (1): ----------+------+-----------------------------------------+
-  | PIAFS Protocol      | Bit  |                                         |
-  | Speed configuration |    S | Bit 1 - Protocol Speed                  |
-  |                     |      |         0 - 32K                         |
-  |                     |      |         1 - 64K (default)               |
-  |                     |    V | Bit 2 - Variable Protocol Speed         |
-  |                     |      |         0 - Speed is fix                |
-  |                     |      |         1 - Speed is variable (default) |
-  |                     |      |             OVERWRITES 32k Bit 1        |
-  |                     |    C | Bit 3   0 - Speed Settings according to |
-  |                     |      |             PIAFS specification         |
-  |                     |      |         1 - Speed setting for chinese   |
-  |                     |      |             PIAFS implementation        |
-  |                     |      | Explanation for chinese speed settings: |
-  |                     |      |         if Bit 3 is set the following   |
-  |                     |      |         rules apply:                    |
-  |                     |      |         Bit1=0 Bit2=0: 32k fix          |
-  |                     |      |         Bit1=1 Bit2=0: 64k fix          |
-  |                     |      |         Bit1=0 Bit2=1: PIAFS is trying  |
-  |                     |      |             to negotiate 32k is that is |
-  |                     |      |             not possible it tries to    |
-  |                     |      |             negotiate 64k               |
-  |                     |      |         Bit1=1 Bit2=1: PIAFS is trying  |
-  |                     |      |             to negotiate 64k is that is |
-  |                     |      |             not possible it tries to    |
-  |                     |      |             negotiate 32k               |
-  + Note (2): ----------+------+-----------------------------------------+
-  | PIAFS               | Bit  | this byte defines the usage of UDATA    |
-  | Implementation      |      | control communication                   |
-  | UDATA usage         |    D | Bit 1 - DCD-ON signalling               |
-  |                     |      |         0 - no DCD-ON is signalled      |
-  |                     |      |             (default)                   |
-  |                     |      |         1 - DCD-ON will be signalled    |
-  |                     |    U | Bit 8 - DDI test application UDATA      |
-  |                     |      |         control communication           |
-  |                     |      |         0 - no UDATA control            |
-  |                     |      |             communication (default)     |
-  |                     |      |             sets as well the DCD-ON     |
-  |                     |      |             signalling                  |
-  |                     |      |         1 - UDATA control communication |
-  |                     |      |             ATTENTION: Do not use these |
-  |                     |      |                        setting if you   |
-  |                     |      |                        are not really   |
-  |                     |      |                        that you need it |
-  |                     |      |                        and you know     |
-  |                     |      |                        exactly what you |
-  |                     |      |                        are doing.       |
-  |                     |      |                        You can easily   |
-  |                     |      |                        disable any      |
-  |                     |      |                        data transfer.   |
-  +---------------------+------+-----------------------------------------+
-*/
-/* ------------------------------------------------------
-   LISTENER DLC DEFINITIONS
-   ------------------------------------------------------ */
-#define LISTENER_FEATURE_MASK_CUMMULATIVE            0x0001
-/* ------------------------------------------------------
-   LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS
-   ------------------------------------------------------ */
-#define META_CODE_LL_UDATA_RX 0x01
-#define META_CODE_LL_UDATA_TX 0x02
-#define META_CODE_LL_DATA_RX  0x03
-#define META_CODE_LL_DATA_TX  0x04
-#define META_CODE_LL_MDATA_RX 0x05
-#define META_CODE_LL_MDATA_TX 0x06
-#define META_CODE_EMPTY       0x10
-#define META_CODE_LOST_FRAMES 0x11
-#define META_FLAG_TRUNCATED   0x0001
-/*------------------------------------------------------------------*/
-/* CAPI-like profile to indicate features on LAW_REQ                */
-/*------------------------------------------------------------------*/
-#define GL_INTERNAL_CONTROLLER_SUPPORTED     0x00000001L
-#define GL_EXTERNAL_EQUIPMENT_SUPPORTED      0x00000002L
-#define GL_HANDSET_SUPPORTED                 0x00000004L
-#define GL_DTMF_SUPPORTED                    0x00000008L
-#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED  0x00000010L
-#define GL_CHANNEL_ALLOCATION_SUPPORTED      0x00000020L
-#define GL_BCHANNEL_OPERATION_SUPPORTED      0x00000040L
-#define GL_LINE_INTERCONNECT_SUPPORTED       0x00000080L
-#define B1_HDLC_SUPPORTED                    0x00000001L
-#define B1_TRANSPARENT_SUPPORTED             0x00000002L
-#define B1_V110_ASYNC_SUPPORTED              0x00000004L
-#define B1_V110_SYNC_SUPPORTED               0x00000008L
-#define B1_T30_SUPPORTED                     0x00000010L
-#define B1_HDLC_INVERTED_SUPPORTED           0x00000020L
-#define B1_TRANSPARENT_R_SUPPORTED           0x00000040L
-#define B1_MODEM_ALL_NEGOTIATE_SUPPORTED     0x00000080L
-#define B1_MODEM_ASYNC_SUPPORTED             0x00000100L
-#define B1_MODEM_SYNC_HDLC_SUPPORTED         0x00000200L
-#define B2_X75_SUPPORTED                     0x00000001L
-#define B2_TRANSPARENT_SUPPORTED             0x00000002L
-#define B2_SDLC_SUPPORTED                    0x00000004L
-#define B2_LAPD_SUPPORTED                    0x00000008L
-#define B2_T30_SUPPORTED                     0x00000010L
-#define B2_PPP_SUPPORTED                     0x00000020L
-#define B2_TRANSPARENT_NO_CRC_SUPPORTED      0x00000040L
-#define B2_MODEM_EC_COMPRESSION_SUPPORTED    0x00000080L
-#define B2_X75_V42BIS_SUPPORTED              0x00000100L
-#define B2_V120_ASYNC_SUPPORTED              0x00000200L
-#define B2_V120_ASYNC_V42BIS_SUPPORTED       0x00000400L
-#define B2_V120_BIT_TRANSPARENT_SUPPORTED    0x00000800L
-#define B2_LAPD_FREE_SAPI_SEL_SUPPORTED      0x00001000L
-#define B3_TRANSPARENT_SUPPORTED             0x00000001L
-#define B3_T90NL_SUPPORTED                   0x00000002L
-#define B3_ISO8208_SUPPORTED                 0x00000004L
-#define B3_X25_DCE_SUPPORTED                 0x00000008L
-#define B3_T30_SUPPORTED                     0x00000010L
-#define B3_T30_WITH_EXTENSIONS_SUPPORTED     0x00000020L
-#define B3_RESERVED_SUPPORTED                0x00000040L
-#define B3_MODEM_SUPPORTED                   0x00000080L
-#define MANUFACTURER_FEATURE_SLAVE_CODEC          0x00000001L
-#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS   0x00000002L
-#define MANUFACTURER_FEATURE_HARDDTMF             0x00000004L
-#define MANUFACTURER_FEATURE_SOFTDTMF_SEND        0x00000008L
-#define MANUFACTURER_FEATURE_DTMF_PARAMETERS      0x00000010L
-#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE     0x00000020L
-#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD      0x00000040L
-#define MANUFACTURER_FEATURE_V18                  0x00000080L
-#define MANUFACTURER_FEATURE_MIXER_CH_CH          0x00000100L
-#define MANUFACTURER_FEATURE_MIXER_CH_PC          0x00000200L
-#define MANUFACTURER_FEATURE_MIXER_PC_CH          0x00000400L
-#define MANUFACTURER_FEATURE_MIXER_PC_PC          0x00000800L
-#define MANUFACTURER_FEATURE_ECHO_CANCELLER       0x00001000L
-#define MANUFACTURER_FEATURE_RTP                  0x00002000L
-#define MANUFACTURER_FEATURE_T38                  0x00004000L
-#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
-#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL  0x00010000L
-#define MANUFACTURER_FEATURE_OOB_CHANNEL          0x00020000L
-#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL      0x00040000L
-#define MANUFACTURER_FEATURE_IN_BAND_FEATURE      0x00080000L
-#define MANUFACTURER_FEATURE_PIAFS                0x00100000L
-#define MANUFACTURER_FEATURE_DTMF_TONE            0x00200000L
-#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS    0x00400000L
-#define MANUFACTURER_FEATURE_OK_FC_LABEL          0x00800000L
-#define MANUFACTURER_FEATURE_VOWN                 0x01000000L
-#define MANUFACTURER_FEATURE_XCONNECT             0x02000000L
-#define MANUFACTURER_FEATURE_DMACONNECT           0x04000000L
-#define MANUFACTURER_FEATURE_AUDIO_TAP            0x08000000L
-#define MANUFACTURER_FEATURE_FAX_NONSTANDARD      0x10000000L
-#define MANUFACTURER_FEATURE_SS7                  0x20000000L
-#define MANUFACTURER_FEATURE_MADAPTER             0x40000000L
-#define MANUFACTURER_FEATURE_MEASURE              0x80000000L
-#define MANUFACTURER_FEATURE2_LISTENING           0x00000001L
-#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L
-#define MANUFACTURER_FEATURE2_GENERIC_TONE        0x00000004L
-#define MANUFACTURER_FEATURE2_COLOR_FAX           0x00000008L
-#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L
-#define RTP_PRIM_PAYLOAD_PCMU_8000     0
-#define RTP_PRIM_PAYLOAD_1016_8000     1
-#define RTP_PRIM_PAYLOAD_G726_32_8000  2
-#define RTP_PRIM_PAYLOAD_GSM_8000      3
-#define RTP_PRIM_PAYLOAD_G723_8000     4
-#define RTP_PRIM_PAYLOAD_DVI4_8000     5
-#define RTP_PRIM_PAYLOAD_DVI4_16000    6
-#define RTP_PRIM_PAYLOAD_LPC_8000      7
-#define RTP_PRIM_PAYLOAD_PCMA_8000     8
-#define RTP_PRIM_PAYLOAD_G722_16000    9
-#define RTP_PRIM_PAYLOAD_QCELP_8000    12
-#define RTP_PRIM_PAYLOAD_G728_8000     14
-#define RTP_PRIM_PAYLOAD_G729_8000     18
-#define RTP_PRIM_PAYLOAD_GSM_HR_8000   30
-#define RTP_PRIM_PAYLOAD_GSM_EFR_8000  31
-#define RTP_ADD_PAYLOAD_BASE           32
-#define RTP_ADD_PAYLOAD_RED            32
-#define RTP_ADD_PAYLOAD_CN_8000        33
-#define RTP_ADD_PAYLOAD_DTMF           34
-#define RTP_PRIM_PAYLOAD_PCMU_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_PCMU_8000)
-#define RTP_PRIM_PAYLOAD_1016_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_1016_8000)
-#define RTP_PRIM_PAYLOAD_G726_32_8000_SUPPORTED  (1L << RTP_PRIM_PAYLOAD_G726_32_8000)
-#define RTP_PRIM_PAYLOAD_GSM_8000_SUPPORTED      (1L << RTP_PRIM_PAYLOAD_GSM_8000)
-#define RTP_PRIM_PAYLOAD_G723_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G723_8000)
-#define RTP_PRIM_PAYLOAD_DVI4_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_DVI4_8000)
-#define RTP_PRIM_PAYLOAD_DVI4_16000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_DVI4_16000)
-#define RTP_PRIM_PAYLOAD_LPC_8000_SUPPORTED      (1L << RTP_PRIM_PAYLOAD_LPC_8000)
-#define RTP_PRIM_PAYLOAD_PCMA_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_PCMA_8000)
-#define RTP_PRIM_PAYLOAD_G722_16000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_G722_16000)
-#define RTP_PRIM_PAYLOAD_QCELP_8000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_QCELP_8000)
-#define RTP_PRIM_PAYLOAD_G728_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G728_8000)
-#define RTP_PRIM_PAYLOAD_G729_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G729_8000)
-#define RTP_PRIM_PAYLOAD_GSM_HR_8000_SUPPORTED   (1L << RTP_PRIM_PAYLOAD_GSM_HR_8000)
-#define RTP_PRIM_PAYLOAD_GSM_EFR_8000_SUPPORTED  (1L << RTP_PRIM_PAYLOAD_GSM_EFR_8000)
-#define RTP_ADD_PAYLOAD_RED_SUPPORTED            (1L << (RTP_ADD_PAYLOAD_RED - RTP_ADD_PAYLOAD_BASE))
-#define RTP_ADD_PAYLOAD_CN_8000_SUPPORTED        (1L << (RTP_ADD_PAYLOAD_CN_8000 - RTP_ADD_PAYLOAD_BASE))
-#define RTP_ADD_PAYLOAD_DTMF_SUPPORTED           (1L << (RTP_ADD_PAYLOAD_DTMF - RTP_ADD_PAYLOAD_BASE))
-/* virtual switching definitions */
-#define VSJOIN         1
-#define VSTRANSPORT    2
-#define VSGETPARAMS    3
-#define VSCAD          1
-#define VSRXCPNAME     2
-#define VSCALLSTAT     3
-#define VSINVOKEID    4
-#define VSCLMRKS       5
-#define VSTBCTIDENT    6
-#define VSETSILINKID   7
-#define VSSAMECONTROLLER 8
-/* Errorcodes for VSETSILINKID begin */
-#define VSETSILINKIDRRWC      1
-#define VSETSILINKIDREJECT    2
-#define VSETSILINKIDTIMEOUT   3
-#define VSETSILINKIDFAILCOUNT 4
-#define VSETSILINKIDERROR     5
-/* Errorcodes for VSETSILINKID end */
-/* -----------------------------------------------------------**
-** The PROTOCOL_FEATURE_STRING in feature.h (included         **
-** in prstart.sx and astart.sx) defines capabilities and      **
-** features of the actual protocol code. It's used as a bit   **
-** mask.                                                      **
-** The following Bits are defined:                            **
-** -----------------------------------------------------------*/
-#define PROTCAP_TELINDUS  0x0001  /* Telindus Variant of protocol code   */
-#define PROTCAP_MAN_IF    0x0002  /* Management interface implemented    */
-#define PROTCAP_V_42      0x0004  /* V42 implemented                     */
-#define PROTCAP_V90D      0x0008  /* V.90D (implies up to 384k DSP code) */
-#define PROTCAP_EXTD_FAX  0x0010  /* Extended FAX (ECM, 2D, T6, Polling) */
-#define PROTCAP_EXTD_RXFC 0x0020  /* RxFC (Extd Flow Control), OOB Chnl  */
-#define PROTCAP_VOIP      0x0040  /* VoIP (implies up to 512k DSP code)  */
-#define PROTCAP_CMA_ALLPR 0x0080  /* CMA support for all NL primitives   */
-#define PROTCAP_FREE8     0x0100  /* not used                            */
-#define PROTCAP_FREE9     0x0200  /* not used                            */
-#define PROTCAP_FREE10    0x0400  /* not used                            */
-#define PROTCAP_FREE11    0x0800  /* not used                            */
-#define PROTCAP_FREE12    0x1000  /* not used                            */
-#define PROTCAP_FREE13    0x2000  /* not used                            */
-#define PROTCAP_FREE14    0x4000  /* not used                            */
-#define PROTCAP_EXTENSION 0x8000  /* used for future extensions          */
-/* -----------------------------------------------------------* */
-/* Onhook data transmission ETS30065901 */
-/* Message Type */
-/*#define RESERVED4                 0x4*/
-#define CALL_SETUP                0x80
-#define MESSAGE_WAITING_INDICATOR 0x82
-/*#define RESERVED84                0x84*/
-/*#define RESERVED85                0x85*/
-#define ADVICE_OF_CHARGE          0x86
-/*1111 0001
-  to
-  1111 1111
-  F1H - Reserved for network operator use
-  to
-  FFH*/
-/* Parameter Types */
-#define DATE_AND_TIME                                           1
-#define CLI_PARAMETER_TYPE                                      2
-#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE                  3
-#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE                4
-#define NAME_PARAMETER_TYPE                                     7
-#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8
-#define VISUAL_INDICATOR_PARAMETER_TYPE                         0xb
-#define COMPLEMENTARY_CLI_PARAMETER_TYPE                        0x10
-#define CALL_TYPE_PARAMETER_TYPE                                0x11
-#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE       0x12
-#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE            0x13
-#define FORWARDED_CALL_TYPE_PARAMETER_TYPE                      0x15
-#define TYPE_OF_CALLING_USER_PARAMETER_TYPE                     0x16
-#define REDIRECTING_NUMBER_PARAMETER_TYPE                       0x1a
-#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE       0xe0
-/* -----------------------------------------------------------* */
-#else
-#endif /* PC_H_INCLUDED  } */
diff --git a/drivers/isdn/hardware/eicon/pc_init.h b/drivers/isdn/hardware/eicon/pc_init.h
deleted file mode 100644 (file)
index d1d0086..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef PC_INIT_H_
-#define PC_INIT_H_
-/*------------------------------------------------------------------*/
-/*
-  Initialisation parameters for the card
-  0x0008 <byte> TEI
-  0x0009 <byte> NT2 flag
-  0x000a <byte> Default DID length
-  0x000b <byte> Disable watchdog flag
-  0x000c <byte> Permanent connection flag
-  0x000d <byte> Bit 3-8: L1 Hunt Group/Tristate
-  0x000d <byte> Bit 1: QSig small CR length if set to 1
-  0x000d <byte> Bit 2: QSig small CHI length if set to 1
-  0x000e <byte> Bit 1-3: Stable L2, 0=OnDemand,1=NoDisc,2=permanent
-  0x000e <byte> Bit 4: NT mode
-  0x000e <byte> Bit 5: QSig Channel ID format
-  0x000e <byte> Bit 6: QSig Call Forwarding Allowed Flag
-  0x000e <byte> Bit 7: Disable AutoSPID Flag
-  0x000f <byte> No order check flag
-  0x0010 <byte> Force companding type:0=default,1=a-law,2=u-law
-  0x0012 <byte> Low channel flag
-  0x0013 <byte> Protocol version
-  0x0014 <byte> CRC4 option:0=default,1=double_frm,2=multi_frm,3=auto
-  0x0015 <byte> Bit 0: NoHscx30, Bit 1: Loopback flag, Bit 2: ForceHscx30
-  0x0016 <byte> DSP info
-  0x0017-0x0019 Serial number
-  0x001a <byte> Card type
-  0x0020 <string> OAD 0
-  0x0040 <string> OSA 0
-  0x0060 <string> SPID 0 (if not T.1)
-  0x0060 <struct> if T.1: Robbed Bit Configuration
-  0x0060          length (8)
-  0x0061          RBS Answer Delay
-  0x0062          RBS Config Bit 3, 4:
-  0  0 -> Wink Start
-  1  0 -> Loop Start
-  0  1 -> Ground Start
-  1  1 -> reserved
-  Bit 5, 6:
-  0  0 -> Pulse Dial -> Rotary
-  1  0 -> DTMF
-  0  1 -> MF
-  1  1 -> reserved
-  0x0063          RBS RX Digit Timeout
-  0x0064          RBS Bearer Capability
-  0x0065-0x0069   RBS Debug Mask
-  0x0080 <string> OAD 1
-  0x00a0 <string> OSA 1
-  0x00c0 <string> SPID 1
-  0x00e0 <w-element list> Additional configuration
-*/
-#define PCINIT_END_OF_LIST                0x00
-#define PCINIT_MODEM_GUARD_TONE           0x01
-#define PCINIT_MODEM_MIN_SPEED            0x02
-#define PCINIT_MODEM_MAX_SPEED            0x03
-#define PCINIT_MODEM_PROTOCOL_OPTIONS     0x04
-#define PCINIT_FAX_OPTIONS                0x05
-#define PCINIT_FAX_MAX_SPEED              0x06
-#define PCINIT_MODEM_OPTIONS              0x07
-#define PCINIT_MODEM_NEGOTIATION_MODE     0x08
-#define PCINIT_MODEM_MODULATIONS_MASK     0x09
-#define PCINIT_MODEM_TRANSMIT_LEVEL       0x0a
-#define PCINIT_FAX_DISABLED_RESOLUTIONS   0x0b
-#define PCINIT_FAX_MAX_RECORDING_WIDTH    0x0c
-#define PCINIT_FAX_MAX_RECORDING_LENGTH   0x0d
-#define PCINIT_FAX_MIN_SCANLINE_TIME      0x0e
-#define PCINIT_US_EKTS_CACH_HANDLES       0x0f
-#define PCINIT_US_EKTS_BEGIN_CONF         0x10
-#define PCINIT_US_EKTS_DROP_CONF          0x11
-#define PCINIT_US_EKTS_CALL_TRANSFER      0x12
-#define PCINIT_RINGERTONE_OPTION          0x13
-#define PCINIT_CARD_ADDRESS               0x14
-#define PCINIT_FPGA_FEATURES              0x15
-#define PCINIT_US_EKTS_MWI                0x16
-#define PCINIT_MODEM_SPEAKER_CONTROL      0x17
-#define PCINIT_MODEM_SPEAKER_VOLUME       0x18
-#define PCINIT_MODEM_CARRIER_WAIT_TIME    0x19
-#define PCINIT_MODEM_CARRIER_LOSS_TIME    0x1a
-#define PCINIT_UNCHAN_B_MASK              0x1b
-#define PCINIT_PART68_LIMITER             0x1c
-#define PCINIT_XDI_FEATURES               0x1d
-#define PCINIT_QSIG_DIALECT               0x1e
-#define PCINIT_DISABLE_AUTOSPID_FLAG      0x1f
-#define PCINIT_FORCE_VOICE_MAIL_ALERT     0x20
-#define PCINIT_PIAFS_TURNAROUND_FRAMES    0x21
-#define PCINIT_L2_COUNT                   0x22
-#define PCINIT_QSIG_FEATURES              0x23
-#define PCINIT_NO_SIGNALLING              0x24
-#define PCINIT_CARD_SN                    0x25
-#define PCINIT_CARD_PORT                  0x26
-#define PCINIT_ALERTTO                    0x27
-#define PCINIT_MODEM_EYE_SETUP            0x28
-#define PCINIT_FAX_V34_OPTIONS            0x29
-/*------------------------------------------------------------------*/
-#define PCINIT_MODEM_GUARD_TONE_NONE            0x00
-#define PCINIT_MODEM_GUARD_TONE_550HZ           0x01
-#define PCINIT_MODEM_GUARD_TONE_1800HZ          0x02
-#define PCINIT_MODEM_GUARD_TONE_CHOICES         0x03
-#define PCINIT_MODEMPROT_DISABLE_V42_V42BIS     0x0001
-#define PCINIT_MODEMPROT_DISABLE_MNP_MNP5       0x0002
-#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL       0x0004
-#define PCINIT_MODEMPROT_DISABLE_V42_DETECT     0x0008
-#define PCINIT_MODEMPROT_DISABLE_COMPRESSION    0x0010
-#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x0020
-#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_1200    0x0100
-#define PCINIT_MODEMPROT_BUFFER_IN_V42_DETECT   0x0200
-#define PCINIT_MODEMPROT_DISABLE_V42_SREJ       0x0400
-#define PCINIT_MODEMPROT_DISABLE_MNP3           0x0800
-#define PCINIT_MODEMPROT_DISABLE_MNP4           0x1000
-#define PCINIT_MODEMPROT_DISABLE_MNP10          0x2000
-#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V22BIS  0x4000
-#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V32BIS  0x8000
-#define PCINIT_MODEMCONFIG_LEASED_LINE_MODE     0x00000001L
-#define PCINIT_MODEMCONFIG_4_WIRE_OPERATION     0x00000002L
-#define PCINIT_MODEMCONFIG_DISABLE_BUSY_DETECT  0x00000004L
-#define PCINIT_MODEMCONFIG_DISABLE_CALLING_TONE 0x00000008L
-#define PCINIT_MODEMCONFIG_DISABLE_ANSWER_TONE  0x00000010L
-#define PCINIT_MODEMCONFIG_ENABLE_DIAL_TONE_DET 0x00000020L
-#define PCINIT_MODEMCONFIG_USE_POTS_INTERFACE   0x00000040L
-#define PCINIT_MODEMCONFIG_FORCE_RAY_TAYLOR_FAX 0x00000080L
-#define PCINIT_MODEMCONFIG_DISABLE_RETRAIN      0x00000100L
-#define PCINIT_MODEMCONFIG_DISABLE_STEPDOWN     0x00000200L
-#define PCINIT_MODEMCONFIG_DISABLE_SPLIT_SPEED  0x00000400L
-#define PCINIT_MODEMCONFIG_DISABLE_TRELLIS      0x00000800L
-#define PCINIT_MODEMCONFIG_ALLOW_RDL_TEST_LOOP  0x00001000L
-#define PCINIT_MODEMCONFIG_DISABLE_STEPUP       0x00002000L
-#define PCINIT_MODEMCONFIG_DISABLE_FLUSH_TIMER  0x00004000L
-#define PCINIT_MODEMCONFIG_REVERSE_DIRECTION    0x00008000L
-#define PCINIT_MODEMCONFIG_DISABLE_TX_REDUCTION 0x00010000L
-#define PCINIT_MODEMCONFIG_DISABLE_PRECODING    0x00020000L
-#define PCINIT_MODEMCONFIG_DISABLE_PREEMPHASIS  0x00040000L
-#define PCINIT_MODEMCONFIG_DISABLE_SHAPING      0x00080000L
-#define PCINIT_MODEMCONFIG_DISABLE_NONLINEAR_EN 0x00100000L
-#define PCINIT_MODEMCONFIG_DISABLE_MANUALREDUCT 0x00200000L
-#define PCINIT_MODEMCONFIG_DISABLE_16_POINT_TRN 0x00400000L
-#define PCINIT_MODEMCONFIG_DISABLE_2400_SYMBOLS 0x01000000L
-#define PCINIT_MODEMCONFIG_DISABLE_2743_SYMBOLS 0x02000000L
-#define PCINIT_MODEMCONFIG_DISABLE_2800_SYMBOLS 0x04000000L
-#define PCINIT_MODEMCONFIG_DISABLE_3000_SYMBOLS 0x08000000L
-#define PCINIT_MODEMCONFIG_DISABLE_3200_SYMBOLS 0x10000000L
-#define PCINIT_MODEMCONFIG_DISABLE_3429_SYMBOLS 0x20000000L
-#define PCINIT_MODEM_NEGOTIATE_HIGHEST          0x00
-#define PCINIT_MODEM_NEGOTIATE_DISABLED         0x01
-#define PCINIT_MODEM_NEGOTIATE_IN_CLASS         0x02
-#define PCINIT_MODEM_NEGOTIATE_V100             0x03
-#define PCINIT_MODEM_NEGOTIATE_V8               0x04
-#define PCINIT_MODEM_NEGOTIATE_V8BIS            0x05
-#define PCINIT_MODEM_NEGOTIATE_CHOICES          0x06
-#define PCINIT_MODEMMODULATION_DISABLE_V21      0x00000001L
-#define PCINIT_MODEMMODULATION_DISABLE_V23      0x00000002L
-#define PCINIT_MODEMMODULATION_DISABLE_V22      0x00000004L
-#define PCINIT_MODEMMODULATION_DISABLE_V22BIS   0x00000008L
-#define PCINIT_MODEMMODULATION_DISABLE_V32      0x00000010L
-#define PCINIT_MODEMMODULATION_DISABLE_V32BIS   0x00000020L
-#define PCINIT_MODEMMODULATION_DISABLE_V34      0x00000040L
-#define PCINIT_MODEMMODULATION_DISABLE_V90      0x00000080L
-#define PCINIT_MODEMMODULATION_DISABLE_BELL103  0x00000100L
-#define PCINIT_MODEMMODULATION_DISABLE_BELL212A 0x00000200L
-#define PCINIT_MODEMMODULATION_DISABLE_VFC      0x00000400L
-#define PCINIT_MODEMMODULATION_DISABLE_K56FLEX  0x00000800L
-#define PCINIT_MODEMMODULATION_DISABLE_X2       0x00001000L
-#define PCINIT_MODEMMODULATION_ENABLE_V29FDX    0x00010000L
-#define PCINIT_MODEMMODULATION_ENABLE_V33       0x00020000L
-#define PCINIT_MODEMMODULATION_ENABLE_V90A      0x00040000L
-#define PCINIT_MODEM_TRANSMIT_LEVEL_CHOICES     0x10
-#define PCINIT_MODEM_SPEAKER_OFF                0x00
-#define PCINIT_MODEM_SPEAKER_DURING_TRAIN       0x01
-#define PCINIT_MODEM_SPEAKER_TIL_CONNECT        0x02
-#define PCINIT_MODEM_SPEAKER_ALWAYS_ON          0x03
-#define PCINIT_MODEM_SPEAKER_CHOICES            0x04
-#define PCINIT_MODEM_SPEAKER_VOLUME_MIN         0x00
-#define PCINIT_MODEM_SPEAKER_VOLUME_LOW         0x01
-#define PCINIT_MODEM_SPEAKER_VOLUME_HIGH        0x02
-#define PCINIT_MODEM_SPEAKER_VOLUME_MAX         0x03
-#define PCINIT_MODEM_SPEAKER_VOLUME_CHOICES     0x04
-/*------------------------------------------------------------------*/
-#define PCINIT_FAXCONFIG_DISABLE_FINE           0x0001
-#define PCINIT_FAXCONFIG_DISABLE_ECM            0x0002
-#define PCINIT_FAXCONFIG_ECM_64_BYTES           0x0004
-#define PCINIT_FAXCONFIG_DISABLE_2D_CODING      0x0008
-#define PCINIT_FAXCONFIG_DISABLE_T6_CODING      0x0010
-#define PCINIT_FAXCONFIG_DISABLE_UNCOMPR        0x0020
-#define PCINIT_FAXCONFIG_REFUSE_POLLING         0x0040
-#define PCINIT_FAXCONFIG_HIDE_TOTAL_PAGES       0x0080
-#define PCINIT_FAXCONFIG_HIDE_ALL_HEADLINE      0x0100
-#define PCINIT_FAXCONFIG_HIDE_PAGE_INFO         0x0180
-#define PCINIT_FAXCONFIG_HEADLINE_OPTIONS_MASK  0x0180
-#define PCINIT_FAXCONFIG_DISABLE_FEATURE_FALLBACK 0x0200
-#define PCINIT_FAXCONFIG_V34FAX_CONTROL_RATE_1200 0x0800
-#define PCINIT_FAXCONFIG_DISABLE_V34FAX         0x1000
-#define PCINIT_FAXCONFIG_DISABLE_R8_0770_OR_200 0x01
-#define PCINIT_FAXCONFIG_DISABLE_R8_1540        0x02
-#define PCINIT_FAXCONFIG_DISABLE_R16_1540_OR_400 0x04
-#define PCINIT_FAXCONFIG_DISABLE_R4_0385_OR_100 0x08
-#define PCINIT_FAXCONFIG_DISABLE_300_300        0x10
-#define PCINIT_FAXCONFIG_DISABLE_INCH_BASED     0x40
-#define PCINIT_FAXCONFIG_DISABLE_METRIC_BASED   0x80
-#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A3       0
-#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_B4       1
-#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A4       2
-#define PCINIT_FAXCONFIG_REC_WIDTH_COUNT        3
-#define PCINIT_FAXCONFIG_REC_LENGTH_UNLIMITED   0
-#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_B4      1
-#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_A4      2
-#define PCINIT_FAXCONFIG_REC_LENGTH_COUNT       3
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_00_00_00 0
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_05_05_05 1
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_05_05 2
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_10 3
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_10 4
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_20 5
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_20 6
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_40 7
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_8    8
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_9    9
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_10   10
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_05 11
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_05 12
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_10 13
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_10 14
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_20 15
-#define PCINIT_FAXCONFIG_SCANLINE_TIME_COUNT    16
-#define PCINIT_FAXCONFIG_DISABLE_TX_REDUCTION   0x00010000L
-#define PCINIT_FAXCONFIG_DISABLE_PRECODING      0x00020000L
-#define PCINIT_FAXCONFIG_DISABLE_PREEMPHASIS    0x00040000L
-#define PCINIT_FAXCONFIG_DISABLE_SHAPING        0x00080000L
-#define PCINIT_FAXCONFIG_DISABLE_NONLINEAR_EN   0x00100000L
-#define PCINIT_FAXCONFIG_DISABLE_MANUALREDUCT   0x00200000L
-#define PCINIT_FAXCONFIG_DISABLE_16_POINT_TRN   0x00400000L
-#define PCINIT_FAXCONFIG_DISABLE_2400_SYMBOLS   0x01000000L
-#define PCINIT_FAXCONFIG_DISABLE_2743_SYMBOLS   0x02000000L
-#define PCINIT_FAXCONFIG_DISABLE_2800_SYMBOLS   0x04000000L
-#define PCINIT_FAXCONFIG_DISABLE_3000_SYMBOLS   0x08000000L
-#define PCINIT_FAXCONFIG_DISABLE_3200_SYMBOLS   0x10000000L
-#define PCINIT_FAXCONFIG_DISABLE_3429_SYMBOLS   0x20000000L
-/*--------------------------------------------------------------------------*/
-#define PCINIT_XDI_CMA_FOR_ALL_NL_PRIMITIVES    0x01
-/*--------------------------------------------------------------------------*/
-#define PCINIT_FPGA_PLX_ACCESS_SUPPORTED        0x01
-/*--------------------------------------------------------------------------*/
-#endif
-/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/pc_maint.h b/drivers/isdn/hardware/eicon/pc_maint.h
deleted file mode 100644 (file)
index 496f018..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifdef PLATFORM_GT_32BIT
-/* #define POINTER_32BIT byte * __ptr32 */
-#define POINTER_32BIT dword
-#else
-#define POINTER_32BIT byte *
-#endif
-#if !defined(MIPS_SCOM)
-#define BUFFER_SZ  48
-#define MAINT_OFFS 0x380
-#else
-#define BUFFER_SZ  128
-#if defined(PRI)
-#define MAINT_OFFS 0xef00
-#else
-#define MAINT_OFFS 0xff00
-#endif
-#endif
-#define MIPS_BUFFER_SZ  128
-#if defined(PRI)
-#define MIPS_MAINT_OFFS 0xef00
-#else
-#define MIPS_MAINT_OFFS 0xff00
-#endif
-#define LOG                     1
-#define MEMR                    2
-#define MEMW                    3
-#define IOR                     4
-#define IOW                     5
-#define B1TEST                  6
-#define B2TEST                  7
-#define BTESTOFF                8
-#define DSIG_STATS              9
-#define B_CH_STATS              10
-#define D_CH_STATS              11
-#define BL1_STATS               12
-#define BL1_STATS_C             13
-#define GET_VERSION             14
-#define OS_STATS                15
-#define XLOG_SET_MASK           16
-#define XLOG_GET_MASK           17
-#define DSP_READ                20
-#define DSP_WRITE               21
-#define OK 0xff
-#define MORE_EVENTS 0xfe
-#define NO_EVENT 1
-struct DSigStruc
-{
-       byte Id;
-       byte u;
-       byte listen;
-       byte active;
-       byte sin[3];
-       byte bc[6];
-       byte llc[6];
-       byte hlc[6];
-       byte oad[20];
-};
-struct BL1Struc {
-       dword cx_b1;
-       dword cx_b2;
-       dword cr_b1;
-       dword cr_b2;
-       dword px_b1;
-       dword px_b2;
-       dword pr_b1;
-       dword pr_b2;
-       word er_b1;
-       word er_b2;
-};
-struct L2Struc {
-       dword XTotal;
-       dword RTotal;
-       word XError;
-       word RError;
-};
-struct OSStruc {
-       dword free_n;
-};
-typedef union
-{
-       struct DSigStruc DSigStats;
-       struct BL1Struc BL1Stats;
-       struct L2Struc L2Stats;
-       struct OSStruc OSStats;
-       byte   b[BUFFER_SZ];
-       word   w[BUFFER_SZ >> 1];
-       word   l[BUFFER_SZ >> 2]; /* word is wrong, do not use! Use 'd' instead. */
-       dword  d[BUFFER_SZ >> 2];
-} BUFFER;
-typedef union
-{
-       struct DSigStruc DSigStats;
-       struct BL1Struc BL1Stats;
-       struct L2Struc L2Stats;
-       struct OSStruc OSStats;
-       byte   b[MIPS_BUFFER_SZ];
-       word   w[MIPS_BUFFER_SZ >> 1];
-       word   l[BUFFER_SZ >> 2]; /* word is wrong, do not use! Use 'd' instead. */
-       dword  d[MIPS_BUFFER_SZ >> 2];
-} MIPS_BUFFER;
-#if !defined(MIPS_SCOM)
-struct pc_maint
-{
-       byte req;
-       byte rc;
-       POINTER_32BIT mem;
-       short length;
-       word port;
-       byte fill[6];
-       BUFFER data;
-};
-#else
-struct pc_maint
-{
-       byte req;
-       byte rc;
-       byte reserved[2];     /* R3000 alignment ... */
-       POINTER_32BIT mem;
-       short length;
-       word port;
-       byte fill[4];         /* data at offset 16   */
-       BUFFER data;
-};
-#endif
-struct mi_pc_maint
-{
-       byte req;
-       byte rc;
-       byte reserved[2];     /* R3000 alignment ... */
-       POINTER_32BIT mem;
-       short length;
-       word port;
-       byte fill[4];         /* data at offset 16   */
-       MIPS_BUFFER data;
-};
diff --git a/drivers/isdn/hardware/eicon/pkmaint.h b/drivers/isdn/hardware/eicon/pkmaint.h
deleted file mode 100644 (file)
index cf3fb14..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
-#define __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
-
-
-/*
-  Only one purpose of this compiler dependent file to pack
-  structures, described in pc_maint.h so that no padding
-  will be included.
-
-  With microsoft compile it is done by "pshpack1.h" and
-  after is restored by "poppack.h"
-*/
-
-
-#include "pc_maint.h"
-
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/platform.h b/drivers/isdn/hardware/eicon/platform.h
deleted file mode 100644 (file)
index 62e2073..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-/* $Id: platform.h,v 1.37.4.6 2005/01/31 12:22:20 armin Exp $
- *
- * platform.h
- *
- *
- * Copyright 2000-2003  by Armin Schindler (mac@melware.de)
- * Copyright 2000  Eicon Networks
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-
-#ifndef        __PLATFORM_H__
-#define        __PLATFORM_H__
-
-#if !defined(DIVA_BUILD)
-#define DIVA_BUILD "local"
-#endif
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/skbuff.h>
-#include <linux/vmalloc.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/list.h>
-#include <asm/types.h>
-#include <asm/io.h>
-
-#include "cardtype.h"
-
-/* activate debuglib for modules only */
-#ifndef MODULE
-#define DIVA_NO_DEBUGLIB
-#endif
-
-#define DIVA_USER_MODE_CARD_CONFIG 1
-#define        USE_EXTENDED_DEBUGS 1
-
-#define MAX_ADAPTER     32
-
-#define DIVA_ISTREAM 1
-
-#define MEMORY_SPACE_TYPE  0
-#define PORT_SPACE_TYPE    1
-
-
-#include <linux/string.h>
-
-#ifndef        byte
-#define        byte   u8
-#endif
-
-#ifndef        word
-#define        word   u16
-#endif
-
-#ifndef        dword
-#define        dword  u32
-#endif
-
-#ifndef        qword
-#define        qword  u64
-#endif
-
-#ifndef        NULL
-#define        NULL    ((void *) 0)
-#endif
-
-#ifndef        far
-#define far
-#endif
-
-#ifndef        _pascal
-#define _pascal
-#endif
-
-#ifndef        _loadds
-#define _loadds
-#endif
-
-#ifndef        _cdecl
-#define _cdecl
-#endif
-
-#define MEM_TYPE_RAM           0
-#define MEM_TYPE_PORT          1
-#define MEM_TYPE_PROM          2
-#define MEM_TYPE_CTLREG                3
-#define MEM_TYPE_RESET         4
-#define MEM_TYPE_CFG           5
-#define MEM_TYPE_ADDRESS       6
-#define MEM_TYPE_CONFIG                7
-#define MEM_TYPE_CONTROL       8
-
-#define MAX_MEM_TYPE           10
-
-#define DIVA_OS_MEM_ATTACH_RAM(a)      ((a)->ram)
-#define DIVA_OS_MEM_ATTACH_PORT(a)     ((a)->port)
-#define DIVA_OS_MEM_ATTACH_PROM(a)     ((a)->prom)
-#define DIVA_OS_MEM_ATTACH_CTLREG(a)   ((a)->ctlReg)
-#define DIVA_OS_MEM_ATTACH_RESET(a)    ((a)->reset)
-#define DIVA_OS_MEM_ATTACH_CFG(a)      ((a)->cfg)
-#define DIVA_OS_MEM_ATTACH_ADDRESS(a)  ((a)->Address)
-#define DIVA_OS_MEM_ATTACH_CONFIG(a)   ((a)->Config)
-#define DIVA_OS_MEM_ATTACH_CONTROL(a)  ((a)->Control)
-
-#define DIVA_OS_MEM_DETACH_RAM(a, x)   do { } while (0)
-#define DIVA_OS_MEM_DETACH_PORT(a, x)  do { } while (0)
-#define DIVA_OS_MEM_DETACH_PROM(a, x)  do { } while (0)
-#define DIVA_OS_MEM_DETACH_CTLREG(a, x)        do { } while (0)
-#define DIVA_OS_MEM_DETACH_RESET(a, x) do { } while (0)
-#define DIVA_OS_MEM_DETACH_CFG(a, x)   do { } while (0)
-#define DIVA_OS_MEM_DETACH_ADDRESS(a, x)       do { } while (0)
-#define DIVA_OS_MEM_DETACH_CONFIG(a, x)        do { } while (0)
-#define DIVA_OS_MEM_DETACH_CONTROL(a, x)       do { } while (0)
-
-#define DIVA_INVALID_FILE_HANDLE  ((dword)(-1))
-
-#define DIVAS_CONTAINING_RECORD(address, type, field)                  \
-       ((type *)((char *)(address) - (char *)(&((type *)0)->field)))
-
-extern int sprintf(char *, const char *, ...);
-
-typedef void *LIST_ENTRY;
-
-typedef char DEVICE_NAME[64];
-typedef struct _ISDN_ADAPTER ISDN_ADAPTER;
-typedef struct _ISDN_ADAPTER *PISDN_ADAPTER;
-
-typedef void (*DIVA_DI_PRINTF)(unsigned char *, ...);
-#include "debuglib.h"
-
-#define dtrc(p) DBG_PRV0(p)
-#define dbug(a, p) DBG_PRV1(p)
-
-
-typedef struct e_info_s E_INFO;
-
-typedef char diva_os_dependent_devica_name_t[64];
-typedef void *PDEVICE_OBJECT;
-
-struct _diva_os_soft_isr;
-struct _diva_os_timer;
-struct _ISDN_ADAPTER;
-
-void diva_log_info(unsigned char *, ...);
-
-/*
-**  XDI DIDD Interface
-*/
-void diva_xdi_didd_register_adapter(int card);
-void diva_xdi_didd_remove_adapter(int card);
-
-/*
-** memory allocation
-*/
-static __inline__ void *diva_os_malloc(unsigned long flags, unsigned long size)
-{
-       void *ret = NULL;
-
-       if (size) {
-               ret = (void *) vmalloc((unsigned int) size);
-       }
-       return (ret);
-}
-static __inline__ void diva_os_free(unsigned long flags, void *ptr)
-{
-       vfree(ptr);
-}
-
-/*
-** use skbuffs for message buffer
-*/
-typedef struct sk_buff diva_os_message_buffer_s;
-diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size, void **data_buf);
-void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb);
-#define DIVA_MESSAGE_BUFFER_LEN(x) x->len
-#define DIVA_MESSAGE_BUFFER_DATA(x) x->data
-
-/*
-** mSeconds waiting
-*/
-static __inline__ void diva_os_sleep(dword mSec)
-{
-       msleep(mSec);
-}
-static __inline__ void diva_os_wait(dword mSec)
-{
-       mdelay(mSec);
-}
-
-/*
-**  PCI Configuration space access
-*/
-void PCIwrite(byte bus, byte func, int offset, void *data, int length, void *pci_dev_handle);
-void PCIread(byte bus, byte func, int offset, void *data, int length, void *pci_dev_handle);
-
-/*
-**  I/O Port utilities
-*/
-int diva_os_register_io_port(void *adapter, int reg, unsigned long port,
-                            unsigned long length, const char *name, int id);
-/*
-**  I/O port access abstraction
-*/
-byte inpp(void __iomem *);
-word inppw(void __iomem *);
-void inppw_buffer(void __iomem *, void *, int);
-void outppw(void __iomem *, word);
-void outppw_buffer(void __iomem * , void*, int);
-void outpp(void __iomem *, word);
-
-/*
-**  IRQ
-*/
-typedef struct _diva_os_adapter_irq_info {
-       byte irq_nr;
-       int  registered;
-       char irq_name[24];
-} diva_os_adapter_irq_info_t;
-int diva_os_register_irq(void *context, byte irq, const char *name);
-void diva_os_remove_irq(void *context, byte irq);
-
-#define diva_os_in_irq() in_irq()
-
-/*
-**  Spin Lock framework
-*/
-typedef long diva_os_spin_lock_magic_t;
-typedef spinlock_t diva_os_spin_lock_t;
-static __inline__ int diva_os_initialize_spin_lock(spinlock_t *lock, void *unused) { \
-       spin_lock_init(lock); return (0); }
-static __inline__ void diva_os_enter_spin_lock(diva_os_spin_lock_t *a, \
-                                              diva_os_spin_lock_magic_t *old_irql, \
-                                              void *dbg) { spin_lock_bh(a); }
-static __inline__ void diva_os_leave_spin_lock(diva_os_spin_lock_t *a, \
-                                              diva_os_spin_lock_magic_t *old_irql, \
-                                              void *dbg) { spin_unlock_bh(a); }
-
-#define diva_os_destroy_spin_lock(a, b) do { } while (0)
-
-/*
-**  Deffered processing framework
-*/
-typedef int (*diva_os_isr_callback_t)(struct _ISDN_ADAPTER *);
-typedef void (*diva_os_soft_isr_callback_t)(struct _diva_os_soft_isr *psoft_isr, void *context);
-
-typedef struct _diva_os_soft_isr {
-       void *object;
-       diva_os_soft_isr_callback_t callback;
-       void *callback_context;
-       char dpc_thread_name[24];
-} diva_os_soft_isr_t;
-
-int diva_os_initialize_soft_isr(diva_os_soft_isr_t *psoft_isr, diva_os_soft_isr_callback_t callback, void *callback_context);
-int diva_os_schedule_soft_isr(diva_os_soft_isr_t *psoft_isr);
-int diva_os_cancel_soft_isr(diva_os_soft_isr_t *psoft_isr);
-void diva_os_remove_soft_isr(diva_os_soft_isr_t *psoft_isr);
-
-/*
-  Get time service
-*/
-void diva_os_get_time(dword *sec, dword *usec);
-
-/*
-**  atomic operation, fake because we use threads
-*/
-typedef int diva_os_atomic_t;
-static inline diva_os_atomic_t
-diva_os_atomic_increment(diva_os_atomic_t *pv)
-{
-       *pv += 1;
-       return (*pv);
-}
-static inline diva_os_atomic_t
-diva_os_atomic_decrement(diva_os_atomic_t *pv)
-{
-       *pv -= 1;
-       return (*pv);
-}
-
-/*
-**  CAPI SECTION
-*/
-#define NO_CORNETN
-#define IMPLEMENT_DTMF 1
-#define IMPLEMENT_ECHO_CANCELLER 1
-#define IMPLEMENT_RTP 1
-#define IMPLEMENT_T38 1
-#define IMPLEMENT_FAX_SUB_SEP_PWD 1
-#define IMPLEMENT_V18 1
-#define IMPLEMENT_DTMF_TONE 1
-#define IMPLEMENT_PIAFS 1
-#define IMPLEMENT_FAX_PAPER_FORMATS 1
-#define IMPLEMENT_VOWN 1
-#define IMPLEMENT_CAPIDTMF 1
-#define IMPLEMENT_FAX_NONSTANDARD 1
-#define VSWITCH_SUPPORT 1
-
-#define IMPLEMENT_MARKED_OK_AFTER_FC 1
-
-#define DIVA_IDI_RX_DMA 1
-
-/*
-** endian macros
-**
-** If only...  In some cases we did use them for endianness conversion;
-** unfortunately, other uses were real iomem accesses.
-*/
-#define READ_BYTE(addr)   readb(addr)
-#define READ_WORD(addr)   readw(addr)
-#define READ_DWORD(addr)  readl(addr)
-
-#define WRITE_BYTE(addr, v)  writeb(v, addr)
-#define WRITE_WORD(addr, v)  writew(v, addr)
-#define WRITE_DWORD(addr, v) writel(v, addr)
-
-static inline __u16 GET_WORD(void *addr)
-{
-       return le16_to_cpu(*(__le16 *)addr);
-}
-static inline __u32 GET_DWORD(void *addr)
-{
-       return le32_to_cpu(*(__le32 *)addr);
-}
-static inline void PUT_WORD(void *addr, __u16 v)
-{
-       *(__le16 *)addr = cpu_to_le16(v);
-}
-static inline void PUT_DWORD(void *addr, __u32 v)
-{
-       *(__le32 *)addr = cpu_to_le32(v);
-}
-
-/*
-** 32/64 bit macors
-*/
-#ifdef BITS_PER_LONG
-#if BITS_PER_LONG > 32
-#define PLATFORM_GT_32BIT
-#define ULongToPtr(x) (void *)(unsigned long)(x)
-#endif
-#endif
-
-/*
-** undef os definitions of macros we use
-*/
-#undef ID_MASK
-#undef N_DATA
-#undef ADDR
-
-/*
-** dump file
-*/
-#define diva_os_dump_file_t char
-#define diva_os_board_trace_t char
-#define diva_os_dump_file(__x__) do { } while (0)
-
-/*
-** size of internal arrays
-*/
-#define MAX_DESCRIPTORS 64
-
-#endif /* __PLATFORM_H__ */
diff --git a/drivers/isdn/hardware/eicon/pr_pc.h b/drivers/isdn/hardware/eicon/pr_pc.h
deleted file mode 100644 (file)
index a08d6d5..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-struct pr_ram {
-       word NextReq;         /* pointer to next Req Buffer               */
-       word NextRc;          /* pointer to next Rc Buffer                */
-       word NextInd;         /* pointer to next Ind Buffer               */
-       byte ReqInput;        /* number of Req Buffers sent               */
-       byte ReqOutput;       /* number of Req Buffers returned           */
-       byte ReqReserved;     /* number of Req Buffers reserved           */
-       byte Int;             /* ISDN-P interrupt                         */
-       byte XLock;           /* Lock field for arbitration               */
-       byte RcOutput;        /* number of Rc buffers received            */
-       byte IndOutput;       /* number of Ind buffers received           */
-       byte IMask;           /* Interrupt Mask Flag                      */
-       byte Reserved1[2];    /* reserved field, do not use               */
-       byte ReadyInt;        /* request field for ready interrupt        */
-       byte Reserved2[12];   /* reserved field, do not use               */
-       byte InterfaceType;   /* interface type 1=16K interface           */
-       word Signature;       /* ISDN-P initialized indication            */
-       byte B[1];            /* buffer space for Req,Ind and Rc          */
-};
-typedef struct {
-       word next;
-       byte Req;
-       byte ReqId;
-       byte ReqCh;
-       byte Reserved1;
-       word Reference;
-       byte Reserved[8];
-       PBUFFER XBuffer;
-} REQ;
-typedef struct {
-       word next;
-       byte Rc;
-       byte RcId;
-       byte RcCh;
-       byte Reserved1;
-       word Reference;
-       byte Reserved2[8];
-} RC;
-typedef struct {
-       word next;
-       byte Ind;
-       byte IndId;
-       byte IndCh;
-       byte MInd;
-       word MLength;
-       word Reference;
-       byte RNR;
-       byte Reserved;
-       dword Ack;
-       PBUFFER RBuffer;
-} IND;
diff --git a/drivers/isdn/hardware/eicon/s_4bri.c b/drivers/isdn/hardware/eicon/s_4bri.c
deleted file mode 100644 (file)
index ec12165..0000000
+++ /dev/null
@@ -1,510 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di.h"
-#include "mi_pc.h"
-#include "pc_maint.h"
-#include "divasync.h"
-#include "pc_init.h"
-#include "io.h"
-#include "helpers.h"
-#include "dsrv4bri.h"
-#include "dsp_defs.h"
-#include "sdp_hdr.h"
-
-/*****************************************************************************/
-#define        MAX_XLOG_SIZE   (64 * 1024)
-
-/* --------------------------------------------------------------------------
-   Recovery XLOG from QBRI Card
-   -------------------------------------------------------------------------- */
-static void qBri_cpu_trapped(PISDN_ADAPTER IoAdapter) {
-       byte  __iomem *base;
-       word *Xlog;
-       dword   regs[4], TrapID, offset, size;
-       Xdesc   xlogDesc;
-       int factor = (IoAdapter->tasks == 1) ? 1 : 2;
-
-/*
- *     check for trapped MIPS 46xx CPU, dump exception frame
- */
-
-       base = DIVA_OS_MEM_ATTACH_CONTROL(IoAdapter);
-       offset = IoAdapter->ControllerNumber * (IoAdapter->MemorySize >> factor);
-
-       TrapID = READ_DWORD(&base[0x80]);
-
-       if ((TrapID == 0x99999999) || (TrapID == 0x99999901))
-       {
-               dump_trap_frame(IoAdapter, &base[0x90]);
-               IoAdapter->trapped = 1;
-       }
-
-       regs[0] = READ_DWORD((base + offset) + 0x70);
-       regs[1] = READ_DWORD((base + offset) + 0x74);
-       regs[2] = READ_DWORD((base + offset) + 0x78);
-       regs[3] = READ_DWORD((base + offset) + 0x7c);
-       regs[0] &= IoAdapter->MemorySize - 1;
-
-       if ((regs[0] >= offset)
-           && (regs[0] < offset + (IoAdapter->MemorySize >> factor) - 1))
-       {
-               if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE))) {
-                       DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
-                       return;
-               }
-
-               size = offset + (IoAdapter->MemorySize >> factor) - regs[0];
-               if (size > MAX_XLOG_SIZE)
-                       size = MAX_XLOG_SIZE;
-               memcpy_fromio(Xlog, &base[regs[0]], size);
-               xlogDesc.buf = Xlog;
-               xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]);
-               xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]);
-               dump_xlog_buffer(IoAdapter, &xlogDesc);
-               diva_os_free(0, Xlog);
-               IoAdapter->trapped = 2;
-       }
-       DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
-}
-
-/* --------------------------------------------------------------------------
-   Reset QBRI Hardware
-   -------------------------------------------------------------------------- */
-static void reset_qBri_hardware(PISDN_ADAPTER IoAdapter) {
-       word volatile __iomem *qBriReset;
-       byte  volatile __iomem *qBriCntrl;
-       byte  volatile __iomem *p;
-
-       qBriReset = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
-       WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_SOFT_RESET);
-       diva_os_wait(1);
-       WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_SOFT_RESET);
-       diva_os_wait(1);
-       WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_RELOAD_EEPROM);
-       diva_os_wait(1);
-       WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_RELOAD_EEPROM);
-       diva_os_wait(1);
-       DIVA_OS_MEM_DETACH_PROM(IoAdapter, qBriReset);
-
-       qBriCntrl = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       p = &qBriCntrl[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
-       WRITE_DWORD(p, 0);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, qBriCntrl);
-
-       DBG_TRC(("resetted board @ reset addr 0x%08lx", qBriReset))
-               DBG_TRC(("resetted board @ cntrl addr 0x%08lx", p))
-               }
-
-/* --------------------------------------------------------------------------
-   Start Card CPU
-   -------------------------------------------------------------------------- */
-void start_qBri_hardware(PISDN_ADAPTER IoAdapter) {
-       byte volatile __iomem *qBriReset;
-       byte volatile __iomem *p;
-
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriReset = &p[(DIVA_4BRI_REVISION(IoAdapter)) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
-       WRITE_DWORD(qBriReset, MQ_RISC_COLD_RESET_MASK);
-       diva_os_wait(2);
-       WRITE_DWORD(qBriReset, MQ_RISC_WARM_RESET_MASK | MQ_RISC_COLD_RESET_MASK);
-       diva_os_wait(10);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-
-       DBG_TRC(("started processor @ addr 0x%08lx", qBriReset))
-               }
-
-/* --------------------------------------------------------------------------
-   Stop Card CPU
-   -------------------------------------------------------------------------- */
-static void stop_qBri_hardware(PISDN_ADAPTER IoAdapter) {
-       byte volatile __iomem *p;
-       dword volatile __iomem *qBriReset;
-       dword volatile __iomem *qBriIrq;
-       dword volatile __iomem *qBriIsacDspReset;
-       int rev2 = DIVA_4BRI_REVISION(IoAdapter);
-       int reset_offset = rev2 ? (MQ2_BREG_RISC)      : (MQ_BREG_RISC);
-       int irq_offset   = rev2 ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST);
-       int hw_offset    = rev2 ? (MQ2_ISAC_DSP_RESET) : (MQ_ISAC_DSP_RESET);
-
-       if (IoAdapter->ControllerNumber > 0)
-               return;
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriReset = (dword volatile __iomem *)&p[reset_offset];
-       qBriIsacDspReset = (dword volatile __iomem *)&p[hw_offset];
-/*
- *     clear interrupt line (reset Local Interrupt Test Register)
- */
-       WRITE_DWORD(qBriReset, 0);
-       WRITE_DWORD(qBriIsacDspReset, 0);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);   /* disable PCI interrupts */
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriIrq   = (dword volatile __iomem *)&p[irq_offset];
-       WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-
-       DBG_TRC(("stopped processor @ addr 0x%08lx", qBriReset))
-
-               }
-
-/* --------------------------------------------------------------------------
-   FPGA download
-   -------------------------------------------------------------------------- */
-#define FPGA_NAME_OFFSET         0x10
-
-static byte *qBri_check_FPGAsrc(PISDN_ADAPTER IoAdapter, char *FileName,
-                               dword *Length, dword *code) {
-       byte *File;
-       char *fpgaFile, *fpgaType, *fpgaDate, *fpgaTime;
-       dword fpgaFlen, fpgaTlen, fpgaDlen, cnt, year, i;
-
-       if (!(File = (byte *)xdiLoadFile(FileName, Length, 0))) {
-               return (NULL);
-       }
-/*
- *      scan file until FF and put id string into buffer
- */
-       for (i = 0; File[i] != 0xff;)
-       {
-               if (++i >= *Length)
-               {
-                       DBG_FTL(("FPGA download: start of data header not found"))
-                               xdiFreeFile(File);
-                       return (NULL);
-               }
-       }
-       *code = i++;
-
-       if ((File[i] & 0xF0) != 0x20)
-       {
-               DBG_FTL(("FPGA download: data header corrupted"))
-                       xdiFreeFile(File);
-               return (NULL);
-       }
-       fpgaFlen = (dword)File[FPGA_NAME_OFFSET - 1];
-       if (fpgaFlen == 0)
-               fpgaFlen = 12;
-       fpgaFile = (char *)&File[FPGA_NAME_OFFSET];
-       fpgaTlen = (dword)fpgaFile[fpgaFlen + 2];
-       if (fpgaTlen == 0)
-               fpgaTlen = 10;
-       fpgaType = (char *)&fpgaFile[fpgaFlen + 3];
-       fpgaDlen = (dword)  fpgaType[fpgaTlen + 2];
-       if (fpgaDlen == 0)
-               fpgaDlen = 11;
-       fpgaDate = (char *)&fpgaType[fpgaTlen + 3];
-       fpgaTime = (char *)&fpgaDate[fpgaDlen + 3];
-       cnt = (dword)(((File[i] & 0x0F) << 20) + (File[i + 1] << 12)
-                     + (File[i + 2] << 4) + (File[i + 3] >> 4));
-
-       if ((dword)(i + (cnt / 8)) > *Length)
-       {
-               DBG_FTL(("FPGA download: '%s' file too small (%ld < %ld)",
-                        FileName, *Length, code + ((cnt + 7) / 8)))
-                       xdiFreeFile(File);
-               return (NULL);
-       }
-       i = 0;
-       do
-       {
-               while ((fpgaDate[i] != '\0')
-                      && ((fpgaDate[i] < '0') || (fpgaDate[i] > '9')))
-               {
-                       i++;
-               }
-               year = 0;
-               while ((fpgaDate[i] >= '0') && (fpgaDate[i] <= '9'))
-                       year = year * 10 + (fpgaDate[i++] - '0');
-       } while ((year < 2000) && (fpgaDate[i] != '\0'));
-
-       switch (IoAdapter->cardType) {
-       case CARDTYPE_DIVASRV_B_2F_PCI:
-               break;
-
-       default:
-               if (year >= 2001) {
-                       IoAdapter->fpga_features |= PCINIT_FPGA_PLX_ACCESS_SUPPORTED;
-               }
-       }
-
-       DBG_LOG(("FPGA[%s] file %s (%s %s) len %d",
-                fpgaType, fpgaFile, fpgaDate, fpgaTime, cnt))
-               return (File);
-}
-
-/******************************************************************************/
-
-#define FPGA_PROG   0x0001             /* PROG enable low */
-#define FPGA_BUSY   0x0002             /* BUSY high, DONE low */
-#define        FPGA_CS     0x000C              /* Enable I/O pins */
-#define FPGA_CCLK   0x0100
-#define FPGA_DOUT   0x0400
-#define FPGA_DIN    FPGA_DOUT   /* bidirectional I/O */
-
-int qBri_FPGA_download(PISDN_ADAPTER IoAdapter) {
-       int            bit;
-       byte           *File;
-       dword          code, FileLength;
-       word volatile __iomem *addr = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
-       word           val, baseval = FPGA_CS | FPGA_PROG;
-
-
-
-       if (DIVA_4BRI_REVISION(IoAdapter))
-       {
-               char *name;
-
-               switch (IoAdapter->cardType) {
-               case CARDTYPE_DIVASRV_B_2F_PCI:
-                       name = "dsbri2f.bit";
-                       break;
-
-               case CARDTYPE_DIVASRV_B_2M_V2_PCI:
-               case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
-                       name = "dsbri2m.bit";
-                       break;
-
-               default:
-                       name = "ds4bri2.bit";
-               }
-
-               File = qBri_check_FPGAsrc(IoAdapter, name,
-                                         &FileLength, &code);
-       }
-       else
-       {
-               File = qBri_check_FPGAsrc(IoAdapter, "ds4bri.bit",
-                                         &FileLength, &code);
-       }
-       if (!File) {
-               DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
-               return (0);
-       }
-/*
- *     prepare download, pulse PROGRAM pin down.
- */
-       WRITE_WORD(addr, baseval & ~FPGA_PROG); /* PROGRAM low pulse */
-       WRITE_WORD(addr, baseval);              /* release */
-       diva_os_wait(50);  /* wait until FPGA finished internal memory clear */
-/*
- *     check done pin, must be low
- */
-       if (READ_WORD(addr) & FPGA_BUSY)
-       {
-               DBG_FTL(("FPGA download: acknowledge for FPGA memory clear missing"))
-                       xdiFreeFile(File);
-               DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
-               return (0);
-       }
-/*
- *     put data onto the FPGA
- */
-       while (code < FileLength)
-       {
-               val = ((word)File[code++]) << 3;
-
-               for (bit = 8; bit-- > 0; val <<= 1) /* put byte onto FPGA */
-               {
-                       baseval &= ~FPGA_DOUT;             /* clr  data bit */
-                       baseval |= (val & FPGA_DOUT);      /* copy data bit */
-                       WRITE_WORD(addr, baseval);
-                       WRITE_WORD(addr, baseval | FPGA_CCLK);     /* set CCLK hi */
-                       WRITE_WORD(addr, baseval | FPGA_CCLK);     /* set CCLK hi */
-                       WRITE_WORD(addr, baseval);                 /* set CCLK lo */
-               }
-       }
-       xdiFreeFile(File);
-       diva_os_wait(100);
-       val = READ_WORD(addr);
-
-       DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
-
-       if (!(val & FPGA_BUSY))
-       {
-               DBG_FTL(("FPGA download: chip remains in busy state (0x%04x)", val))
-                       return (0);
-       }
-
-       return (1);
-}
-
-static int load_qBri_hardware(PISDN_ADAPTER IoAdapter) {
-       return (0);
-}
-
-/* --------------------------------------------------------------------------
-   Card ISR
-   -------------------------------------------------------------------------- */
-static int qBri_ISR(struct _ISDN_ADAPTER *IoAdapter) {
-       dword volatile     __iomem *qBriIrq;
-
-       PADAPTER_LIST_ENTRY QuadroList = IoAdapter->QuadroList;
-
-       word                    i;
-       int                     serviced = 0;
-       byte __iomem *p;
-
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-
-       if (!(READ_BYTE(&p[PLX9054_INTCSR]) & 0x80)) {
-               DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-               return (0);
-       }
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-
-/*
- *     clear interrupt line (reset Local Interrupt Test Register)
- */
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST)]);
-       WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-
-       for (i = 0; i < IoAdapter->tasks; ++i)
-       {
-               IoAdapter = QuadroList->QuadroAdapter[i];
-
-               if (IoAdapter && IoAdapter->Initialized
-                   && IoAdapter->tst_irq(&IoAdapter->a))
-               {
-                       IoAdapter->IrqCount++;
-                       serviced = 1;
-                       diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr);
-               }
-       }
-
-       return (serviced);
-}
-
-/* --------------------------------------------------------------------------
-   Does disable the interrupt on the card
-   -------------------------------------------------------------------------- */
-static void disable_qBri_interrupt(PISDN_ADAPTER IoAdapter) {
-       dword volatile __iomem *qBriIrq;
-       byte __iomem *p;
-
-       if (IoAdapter->ControllerNumber > 0)
-               return;
-/*
- *     clear interrupt line (reset Local Interrupt Test Register)
- */
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);   /* disable PCI interrupts */
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST)]);
-       WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-}
-
-/* --------------------------------------------------------------------------
-   Install Adapter Entry Points
-   -------------------------------------------------------------------------- */
-static void set_common_qBri_functions(PISDN_ADAPTER IoAdapter) {
-       ADAPTER *a;
-
-       a = &IoAdapter->a;
-
-       a->ram_in           = mem_in;
-       a->ram_inw          = mem_inw;
-       a->ram_in_buffer    = mem_in_buffer;
-       a->ram_look_ahead   = mem_look_ahead;
-       a->ram_out          = mem_out;
-       a->ram_outw         = mem_outw;
-       a->ram_out_buffer   = mem_out_buffer;
-       a->ram_inc          = mem_inc;
-
-       IoAdapter->out = pr_out;
-       IoAdapter->dpc = pr_dpc;
-       IoAdapter->tst_irq = scom_test_int;
-       IoAdapter->clr_irq  = scom_clear_int;
-       IoAdapter->pcm  = (struct pc_maint *)MIPS_MAINT_OFFS;
-
-       IoAdapter->load = load_qBri_hardware;
-
-       IoAdapter->disIrq = disable_qBri_interrupt;
-       IoAdapter->rstFnc = reset_qBri_hardware;
-       IoAdapter->stop = stop_qBri_hardware;
-       IoAdapter->trapFnc = qBri_cpu_trapped;
-
-       IoAdapter->diva_isr_handler = qBri_ISR;
-
-       IoAdapter->a.io = (void *)IoAdapter;
-}
-
-static void set_qBri_functions(PISDN_ADAPTER IoAdapter) {
-       if (!IoAdapter->tasks) {
-               IoAdapter->tasks = MQ_INSTANCE_COUNT;
-       }
-       IoAdapter->MemorySize = MQ_MEMORY_SIZE;
-       set_common_qBri_functions(IoAdapter);
-       diva_os_set_qBri_functions(IoAdapter);
-}
-
-static void set_qBri2_functions(PISDN_ADAPTER IoAdapter) {
-       if (!IoAdapter->tasks) {
-               IoAdapter->tasks = MQ_INSTANCE_COUNT;
-       }
-       IoAdapter->MemorySize = (IoAdapter->tasks == 1) ? BRI2_MEMORY_SIZE : MQ2_MEMORY_SIZE;
-       set_common_qBri_functions(IoAdapter);
-       diva_os_set_qBri2_functions(IoAdapter);
-}
-
-/******************************************************************************/
-
-void prepare_qBri_functions(PISDN_ADAPTER IoAdapter) {
-
-       set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[0]);
-       set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[1]);
-       set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[2]);
-       set_qBri_functions(IoAdapter->QuadroList->QuadroAdapter[3]);
-
-}
-
-void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter) {
-       if (!IoAdapter->tasks) {
-               IoAdapter->tasks = MQ_INSTANCE_COUNT;
-       }
-
-       set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[0]);
-       if (IoAdapter->tasks > 1) {
-               set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[1]);
-               set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[2]);
-               set_qBri2_functions(IoAdapter->QuadroList->QuadroAdapter[3]);
-       }
-
-}
-
-/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_bri.c b/drivers/isdn/hardware/eicon/s_bri.c
deleted file mode 100644 (file)
index 6a5bb74..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di.h"
-#include "mi_pc.h"
-#include "pc_maint.h"
-#include "divasync.h"
-#include "io.h"
-#include "helpers.h"
-#include "dsrv_bri.h"
-#include "dsp_defs.h"
-/*****************************************************************************/
-#define MAX_XLOG_SIZE (64 * 1024)
-/* --------------------------------------------------------------------------
-   Investigate card state, recovery trace buffer
-   -------------------------------------------------------------------------- */
-static void bri_cpu_trapped(PISDN_ADAPTER IoAdapter) {
-       byte  __iomem *addrHi, *addrLo, *ioaddr;
-       word *Xlog;
-       dword   regs[4], i, size;
-       Xdesc   xlogDesc;
-       byte __iomem *Port;
-/*
- * first read pointers and trap frame
- */
-       if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE)))
-               return;
-       Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
-       addrHi = Port + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
-       addrLo = Port + ADDR;
-       ioaddr = Port + DATA;
-       outpp(addrHi,  0);
-       outppw(addrLo, 0);
-       for (i = 0; i < 0x100; Xlog[i++] = inppw(ioaddr));
-/*
- * check for trapped MIPS 3xxx CPU, dump only exception frame
- */
-       if (GET_DWORD(&Xlog[0x80 / sizeof(Xlog[0])]) == 0x99999999)
-       {
-               dump_trap_frame(IoAdapter, &((byte *)Xlog)[0x90]);
-               IoAdapter->trapped = 1;
-       }
-       regs[0] = GET_DWORD(&((byte *)Xlog)[0x70]);
-       regs[1] = GET_DWORD(&((byte *)Xlog)[0x74]);
-       regs[2] = GET_DWORD(&((byte *)Xlog)[0x78]);
-       regs[3] = GET_DWORD(&((byte *)Xlog)[0x7c]);
-       outpp(addrHi, (regs[1] >> 16) & 0x7F);
-       outppw(addrLo, regs[1] & 0xFFFF);
-       xlogDesc.cnt = inppw(ioaddr);
-       outpp(addrHi, (regs[2] >> 16) & 0x7F);
-       outppw(addrLo, regs[2] & 0xFFFF);
-       xlogDesc.out = inppw(ioaddr);
-       xlogDesc.buf = Xlog;
-       regs[0] &= IoAdapter->MemorySize - 1;
-       if ((regs[0] < IoAdapter->MemorySize - 1))
-       {
-               size = IoAdapter->MemorySize - regs[0];
-               if (size > MAX_XLOG_SIZE)
-                       size = MAX_XLOG_SIZE;
-               for (i = 0; i < (size / sizeof(*Xlog)); regs[0] += 2)
-               {
-                       outpp(addrHi, (regs[0] >> 16) & 0x7F);
-                       outppw(addrLo, regs[0] & 0xFFFF);
-                       Xlog[i++] = inppw(ioaddr);
-               }
-               dump_xlog_buffer(IoAdapter, &xlogDesc);
-               diva_os_free(0, Xlog);
-               IoAdapter->trapped = 2;
-       }
-       outpp(addrHi, (byte)((BRI_UNCACHED_ADDR(IoAdapter->MemoryBase + IoAdapter->MemorySize -
-                                               BRI_SHARED_RAM_SIZE)) >> 16));
-       outppw(addrLo, 0x00);
-       DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
-}
-/* ---------------------------------------------------------------------
-   Reset hardware
-   --------------------------------------------------------------------- */
-static void reset_bri_hardware(PISDN_ADAPTER IoAdapter) {
-       byte __iomem *p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       outpp(p, 0x00);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-}
-/* ---------------------------------------------------------------------
-   Halt system
-   --------------------------------------------------------------------- */
-static void stop_bri_hardware(PISDN_ADAPTER IoAdapter) {
-       byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       if (p) {
-               outpp(p, 0x00); /* disable interrupts ! */
-       }
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       outpp(p, 0x00);    /* clear int, halt cpu */
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-}
-static int load_bri_hardware(PISDN_ADAPTER IoAdapter) {
-       return (0);
-}
-/******************************************************************************/
-static int bri_ISR(struct _ISDN_ADAPTER *IoAdapter) {
-       byte __iomem *p;
-
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       if (!(inpp(p) & 0x01)) {
-               DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-               return (0);
-       }
-       /*
-         clear interrupt line
-       */
-       outpp(p, 0x08);
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-       IoAdapter->IrqCount++;
-       if (IoAdapter->Initialized) {
-               diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr);
-       }
-       return (1);
-}
-/* --------------------------------------------------------------------------
-   Disable IRQ in the card hardware
-   -------------------------------------------------------------------------- */
-static void disable_bri_interrupt(PISDN_ADAPTER IoAdapter) {
-       byte __iomem *p;
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       if (p)
-       {
-               outpp(p, 0x00); /* disable interrupts ! */
-       }
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-       p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
-       outpp(p, 0x00); /* clear int, halt cpu */
-       DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
-}
-/* -------------------------------------------------------------------------
-   Fill card entry points
-   ------------------------------------------------------------------------- */
-void prepare_maestra_functions(PISDN_ADAPTER IoAdapter) {
-       ADAPTER *a = &IoAdapter->a;
-       a->ram_in             = io_in;
-       a->ram_inw            = io_inw;
-       a->ram_in_buffer      = io_in_buffer;
-       a->ram_look_ahead     = io_look_ahead;
-       a->ram_out            = io_out;
-       a->ram_outw           = io_outw;
-       a->ram_out_buffer     = io_out_buffer;
-       a->ram_inc            = io_inc;
-       IoAdapter->MemoryBase = BRI_MEMORY_BASE;
-       IoAdapter->MemorySize = BRI_MEMORY_SIZE;
-       IoAdapter->out        = pr_out;
-       IoAdapter->dpc        = pr_dpc;
-       IoAdapter->tst_irq    = scom_test_int;
-       IoAdapter->clr_irq    = scom_clear_int;
-       IoAdapter->pcm        = (struct pc_maint *)MIPS_MAINT_OFFS;
-       IoAdapter->load       = load_bri_hardware;
-       IoAdapter->disIrq     = disable_bri_interrupt;
-       IoAdapter->rstFnc     = reset_bri_hardware;
-       IoAdapter->stop       = stop_bri_hardware;
-       IoAdapter->trapFnc    = bri_cpu_trapped;
-       IoAdapter->diva_isr_handler = bri_ISR;
-       /*
-         Prepare OS dependent functions
-       */
-       diva_os_prepare_maestra_functions(IoAdapter);
-}
-/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_pri.c b/drivers/isdn/hardware/eicon/s_pri.c
deleted file mode 100644 (file)
index ddd0e0e..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "pr_pc.h"
-#include "di.h"
-#include "mi_pc.h"
-#include "pc_maint.h"
-#include "divasync.h"
-#include "io.h"
-#include "helpers.h"
-#include "dsrv_pri.h"
-#include "dsp_defs.h"
-/*****************************************************************************/
-#define MAX_XLOG_SIZE  (64 * 1024)
-/* -------------------------------------------------------------------------
-   Does return offset between ADAPTER->ram and real begin of memory
-   ------------------------------------------------------------------------- */
-static dword pri_ram_offset(ADAPTER *a) {
-       return ((dword)MP_SHARED_RAM_OFFSET);
-}
-/* -------------------------------------------------------------------------
-   Recovery XLOG buffer from the card
-   ------------------------------------------------------------------------- */
-static void pri_cpu_trapped(PISDN_ADAPTER IoAdapter) {
-       byte  __iomem *base;
-       word *Xlog;
-       dword   regs[4], TrapID, size;
-       Xdesc   xlogDesc;
-/*
- * check for trapped MIPS 46xx CPU, dump exception frame
- */
-       base   = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
-       TrapID = READ_DWORD(&base[0x80]);
-       if ((TrapID == 0x99999999) || (TrapID == 0x99999901))
-       {
-               dump_trap_frame(IoAdapter, &base[0x90]);
-               IoAdapter->trapped = 1;
-       }
-       regs[0] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x70]);
-       regs[1] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x74]);
-       regs[2] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x78]);
-       regs[3] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x7c]);
-       regs[0] &= IoAdapter->MemorySize - 1;
-       if ((regs[0] < IoAdapter->MemorySize - 1))
-       {
-               if (!(Xlog = (word *)diva_os_malloc(0, MAX_XLOG_SIZE))) {
-                       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
-                       return;
-               }
-               size = IoAdapter->MemorySize - regs[0];
-               if (size > MAX_XLOG_SIZE)
-                       size = MAX_XLOG_SIZE;
-               memcpy_fromio(Xlog, &base[regs[0]], size);
-               xlogDesc.buf = Xlog;
-               xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]);
-               xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]);
-               dump_xlog_buffer(IoAdapter, &xlogDesc);
-               diva_os_free(0, Xlog);
-               IoAdapter->trapped = 2;
-       }
-       DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
-}
-/* -------------------------------------------------------------------------
-   Hardware reset of PRI card
-   ------------------------------------------------------------------------- */
-static void reset_pri_hardware(PISDN_ADAPTER IoAdapter) {
-       byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
-       diva_os_wait(50);
-       WRITE_BYTE(p, 0x00);
-       diva_os_wait(50);
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-}
-/* -------------------------------------------------------------------------
-   Stop Card Hardware
-   ------------------------------------------------------------------------- */
-static void stop_pri_hardware(PISDN_ADAPTER IoAdapter) {
-       dword i;
-       byte __iomem *p;
-       dword volatile __iomem *cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
-       WRITE_DWORD(&cfgReg[3], 0);
-       WRITE_DWORD(&cfgReg[1], 0);
-       DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
-       IoAdapter->a.ram_out(&IoAdapter->a, &RAM->SWReg, SWREG_HALT_CPU);
-       i = 0;
-       while ((i < 100) && (IoAdapter->a.ram_in(&IoAdapter->a, &RAM->SWReg) != 0))
-       {
-               diva_os_wait(1);
-               i++;
-       }
-       DBG_TRC(("%s: PRI stopped (%d)", IoAdapter->Name, i))
-               cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
-       WRITE_DWORD(&cfgReg[0], ((dword)(~0x03E00000)));
-       DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
-       diva_os_wait(1);
-       p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
-       WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
-       DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
-}
-static int load_pri_hardware(PISDN_ADAPTER IoAdapter) {
-       return (0);
-}
-/* --------------------------------------------------------------------------
-   PRI Adapter interrupt Service Routine
-   -------------------------------------------------------------------------- */
-static int pri_ISR(struct _ISDN_ADAPTER *IoAdapter) {
-       byte __iomem *cfg = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
-       if (!(READ_DWORD(cfg) & 0x80000000)) {
-               DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
-               return (0);
-       }
-       /*
-         clear interrupt line
-       */
-       WRITE_DWORD(cfg, (dword)~0x03E00000);
-       DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
-       IoAdapter->IrqCount++;
-       if (IoAdapter->Initialized)
-       {
-               diva_os_schedule_soft_isr(&IoAdapter->isr_soft_isr);
-       }
-       return (1);
-}
-/* -------------------------------------------------------------------------
-   Disable interrupt in the card hardware
-   ------------------------------------------------------------------------- */
-static void disable_pri_interrupt(PISDN_ADAPTER IoAdapter) {
-       dword volatile __iomem *cfgReg = (dword volatile __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
-       WRITE_DWORD(&cfgReg[3], 0);
-       WRITE_DWORD(&cfgReg[1], 0);
-       WRITE_DWORD(&cfgReg[0], (dword)(~0x03E00000));
-       DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
-}
-/* -------------------------------------------------------------------------
-   Install entry points for PRI Adapter
-   ------------------------------------------------------------------------- */
-static void prepare_common_pri_functions(PISDN_ADAPTER IoAdapter) {
-       ADAPTER *a = &IoAdapter->a;
-       a->ram_in           = mem_in;
-       a->ram_inw          = mem_inw;
-       a->ram_in_buffer    = mem_in_buffer;
-       a->ram_look_ahead   = mem_look_ahead;
-       a->ram_out          = mem_out;
-       a->ram_outw         = mem_outw;
-       a->ram_out_buffer   = mem_out_buffer;
-       a->ram_inc          = mem_inc;
-       a->ram_offset       = pri_ram_offset;
-       a->ram_out_dw    = mem_out_dw;
-       a->ram_in_dw    = mem_in_dw;
-       a->istream_wakeup   = pr_stream;
-       IoAdapter->out      = pr_out;
-       IoAdapter->dpc      = pr_dpc;
-       IoAdapter->tst_irq  = scom_test_int;
-       IoAdapter->clr_irq  = scom_clear_int;
-       IoAdapter->pcm      = (struct pc_maint *)(MIPS_MAINT_OFFS
-                                                 - MP_SHARED_RAM_OFFSET);
-       IoAdapter->load     = load_pri_hardware;
-       IoAdapter->disIrq   = disable_pri_interrupt;
-       IoAdapter->rstFnc   = reset_pri_hardware;
-       IoAdapter->stop     = stop_pri_hardware;
-       IoAdapter->trapFnc  = pri_cpu_trapped;
-       IoAdapter->diva_isr_handler = pri_ISR;
-}
-/* -------------------------------------------------------------------------
-   Install entry points for PRI Adapter
-   ------------------------------------------------------------------------- */
-void prepare_pri_functions(PISDN_ADAPTER IoAdapter) {
-       IoAdapter->MemorySize = MP_MEMORY_SIZE;
-       prepare_common_pri_functions(IoAdapter);
-       diva_os_prepare_pri_functions(IoAdapter);
-}
-/* -------------------------------------------------------------------------
-   Install entry points for PRI Rev.2 Adapter
-   ------------------------------------------------------------------------- */
-void prepare_pri2_functions(PISDN_ADAPTER IoAdapter) {
-       IoAdapter->MemorySize = MP2_MEMORY_SIZE;
-       prepare_common_pri_functions(IoAdapter);
-       diva_os_prepare_pri2_functions(IoAdapter);
-}
-/* ------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/sdp_hdr.h b/drivers/isdn/hardware/eicon/sdp_hdr.h
deleted file mode 100644 (file)
index 5e20f8d..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-#ifndef __DIVA_SOFT_DSP_TASK_ENTRY_H__
-#define __DIVA_SOFT_DSP_TASK_ENTRY_H__
-/*
-  The soft DSP image is described by binary header contained on begin of this
-  image:
-  OFFSET FROM IMAGE START |  VARIABLE
-  ------------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_LINK_OFFS   |  link to the next image
-  ----------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_GP_OFFS    |  image gp register value, void*
-  ----------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS   |  diva_mips_sdp_task_entry_t*
-  ----------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS |  image image start address (void*)
-  ----------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS |  image image end address   (void*)
-  ----------------------------------------------------------------------
-  DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS |  image id string char[...];
-  ----------------------------------------------------------------------
-*/
-#define DIVA_MIPS_TASK_IMAGE_LINK_OFFS   0x6C
-#define DIVA_MIPS_TASK_IMAGE_GP_OFFS    0x70
-#define DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS   0x74
-#define DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS 0x78
-#define DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS 0x7c
-#define DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS 0x80
-/*
-  This function is called in order to set GP register of this task
-  This function should be always called before any function of the
-  task is called
-*/
-typedef void (*diva_task_set_prog_gp_proc_t)(void *new_gp);
-/*
-  This function is called to clear .bss at task initialization step
-*/
-typedef void (*diva_task_sys_reset_proc_t)(void);
-/*
-  This function is called in order to provide GP of master call to
-  task, that will be used by calls from the task to the master
-*/
-typedef void (*diva_task_set_main_gp_proc_t)(void *main_gp);
-/*
-  This function is called to provide address of 'dprintf' function
-  to the task
-*/
-typedef word (*diva_prt_proc_t)(char *, ...);
-typedef void (*diva_task_set_prt_proc_t)(diva_prt_proc_t fn);
-/*
-  This function is called to set task PID
-*/
-typedef void (*diva_task_set_pid_proc_t)(dword id);
-/*
-  This function is called for run-time task init
-*/
-typedef int (*diva_task_run_time_init_proc_t)(void*, dword);
-/*
-  This function is called from system scheduler or from timer
-*/
-typedef void (*diva_task_callback_proc_t)(void);
-/*
-  This callback is used by task to get current time im mS
-*/
-typedef dword (*diva_task_get_tick_count_proc_t)(void);
-typedef void (*diva_task_set_get_time_proc_t)(\
-       diva_task_get_tick_count_proc_t fn);
-typedef struct _diva_mips_sdp_task_entry {
-       diva_task_set_prog_gp_proc_t  set_gp_proc;
-       diva_task_sys_reset_proc_t   sys_reset_proc;
-       diva_task_set_main_gp_proc_t  set_main_gp_proc;
-       diva_task_set_prt_proc_t    set_dprintf_proc;
-       diva_task_set_pid_proc_t    set_pid_proc;
-       diva_task_run_time_init_proc_t run_time_init_proc;
-       diva_task_callback_proc_t    task_callback_proc;
-       diva_task_callback_proc_t    timer_callback_proc;
-       diva_task_set_get_time_proc_t  set_get_time_proc;
-       void *last_entry_proc;
-} diva_mips_sdp_task_entry_t;
-/*
-  'last_entry_proc' should be set to zero and is used for future extensuios
-*/
-typedef struct _diva_mips_sw_task {
-       diva_mips_sdp_task_entry_t  sdp_entry;
-       void *sdp_gp_reg;
-       void *own_gp_reg;
-} diva_mips_sw_task_t;
-#if !defined(DIVA_BRI2F_SDP_1_NAME)
-#define DIVA_BRI2F_SDP_1_NAME "sdp0.2q0"
-#endif
-#if !defined(DIVA_BRI2F_SDP_2_NAME)
-#define DIVA_BRI2F_SDP_2_NAME "sdp1.2q0"
-#endif
-#endif
diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c
deleted file mode 100644 (file)
index db4dd4f..0000000
+++ /dev/null
@@ -1,886 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* $Id: um_idi.c,v 1.14 2004/03/21 17:54:37 armin Exp $ */
-
-#include "platform.h"
-#include "di_defs.h"
-#include "pc.h"
-#include "dqueue.h"
-#include "adapter.h"
-#include "entity.h"
-#include "um_xdi.h"
-#include "um_idi.h"
-#include "debuglib.h"
-#include "divasync.h"
-
-#define DIVAS_MAX_XDI_ADAPTERS 64
-
-/* --------------------------------------------------------------------------
-   IMPORTS
-   -------------------------------------------------------------------------- */
-extern void diva_os_wakeup_read(void *os_context);
-extern void diva_os_wakeup_close(void *os_context);
-/* --------------------------------------------------------------------------
-   LOCALS
-   -------------------------------------------------------------------------- */
-static LIST_HEAD(adapter_q);
-static diva_os_spin_lock_t adapter_lock;
-
-static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr);
-static void cleanup_adapter(diva_um_idi_adapter_t *a);
-static void cleanup_entity(divas_um_idi_entity_t *e);
-static int diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t *a,
-                                              diva_um_idi_adapter_features_t
-                                              *features);
-static int process_idi_request(divas_um_idi_entity_t *e,
-                              const diva_um_idi_req_hdr_t *req);
-static int process_idi_rc(divas_um_idi_entity_t *e, byte rc);
-static int process_idi_ind(divas_um_idi_entity_t *e, byte ind);
-static int write_return_code(divas_um_idi_entity_t *e, byte rc);
-
-/* --------------------------------------------------------------------------
-   MAIN
-   -------------------------------------------------------------------------- */
-int diva_user_mode_idi_init(void)
-{
-       diva_os_initialize_spin_lock(&adapter_lock, "adapter");
-       return (0);
-}
-
-/* --------------------------------------------------------------------------
-   Copy adapter features to user supplied buffer
-   -------------------------------------------------------------------------- */
-static int
-diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t *a,
-                                   diva_um_idi_adapter_features_t *
-                                   features)
-{
-       IDI_SYNC_REQ sync_req;
-
-       if ((a) && (a->d.request)) {
-               features->type = a->d.type;
-               features->features = a->d.features;
-               features->channels = a->d.channels;
-               memset(features->name, 0, sizeof(features->name));
-
-               sync_req.GetName.Req = 0;
-               sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
-               (*(a->d.request)) ((ENTITY *)&sync_req);
-               strlcpy(features->name, sync_req.GetName.name,
-                       sizeof(features->name));
-
-               sync_req.GetSerial.Req = 0;
-               sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
-               sync_req.GetSerial.serial = 0;
-               (*(a->d.request))((ENTITY *)&sync_req);
-               features->serial_number = sync_req.GetSerial.serial;
-       }
-
-       return ((a) ? 0 : -1);
-}
-
-/* --------------------------------------------------------------------------
-   REMOVE ADAPTER
-   -------------------------------------------------------------------------- */
-void diva_user_mode_idi_remove_adapter(int adapter_nr)
-{
-       struct list_head *tmp;
-       diva_um_idi_adapter_t *a;
-
-       list_for_each(tmp, &adapter_q) {
-               a = list_entry(tmp, diva_um_idi_adapter_t, link);
-               if (a->adapter_nr == adapter_nr) {
-                       list_del(tmp);
-                       cleanup_adapter(a);
-                       DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
-                       diva_os_free(0, a);
-                       break;
-               }
-       }
-}
-
-/* --------------------------------------------------------------------------
-   CALLED ON DRIVER EXIT (UNLOAD)
-   -------------------------------------------------------------------------- */
-void diva_user_mode_idi_finit(void)
-{
-       struct list_head *tmp, *safe;
-       diva_um_idi_adapter_t *a;
-
-       list_for_each_safe(tmp, safe, &adapter_q) {
-               a = list_entry(tmp, diva_um_idi_adapter_t, link);
-               list_del(tmp);
-               cleanup_adapter(a);
-               DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
-               diva_os_free(0, a);
-       }
-       diva_os_destroy_spin_lock(&adapter_lock, "adapter");
-}
-
-/* -------------------------------------------------------------------------
-   CREATE AND INIT IDI ADAPTER
-   ------------------------------------------------------------------------- */
-int diva_user_mode_idi_create_adapter(const DESCRIPTOR *d, int adapter_nr)
-{
-       diva_os_spin_lock_magic_t old_irql;
-       diva_um_idi_adapter_t *a =
-               (diva_um_idi_adapter_t *) diva_os_malloc(0,
-                                                        sizeof
-                                                        (diva_um_idi_adapter_t));
-
-       if (!a) {
-               return (-1);
-       }
-       memset(a, 0x00, sizeof(*a));
-       INIT_LIST_HEAD(&a->entity_q);
-
-       a->d = *d;
-       a->adapter_nr = adapter_nr;
-
-       DBG_LOG(("DIDD_ADD A(%d), type:%02x, features:%04x, channels:%d",
-                adapter_nr, a->d.type, a->d.features, a->d.channels));
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_adapter");
-       list_add_tail(&a->link, &adapter_q);
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_adapter");
-       return (0);
-}
-
-/* ------------------------------------------------------------------------
-   Find adapter by Adapter number
-   ------------------------------------------------------------------------ */
-static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr)
-{
-       diva_um_idi_adapter_t *a = NULL;
-       struct list_head *tmp;
-
-       list_for_each(tmp, &adapter_q) {
-               a = list_entry(tmp, diva_um_idi_adapter_t, link);
-               DBG_TRC(("find_adapter: (%d)-(%d)", nr, a->adapter_nr));
-               if (a->adapter_nr == (int)nr)
-                       break;
-               a = NULL;
-       }
-       return (a);
-}
-
-/* ------------------------------------------------------------------------
-   Cleanup this adapter and cleanup/delete all entities assigned
-   to this adapter
-   ------------------------------------------------------------------------ */
-static void cleanup_adapter(diva_um_idi_adapter_t *a)
-{
-       struct list_head *tmp, *safe;
-       divas_um_idi_entity_t *e;
-
-       list_for_each_safe(tmp, safe, &a->entity_q) {
-               e = list_entry(tmp, divas_um_idi_entity_t, link);
-               list_del(tmp);
-               cleanup_entity(e);
-               if (e->os_context) {
-                       diva_os_wakeup_read(e->os_context);
-                       diva_os_wakeup_close(e->os_context);
-               }
-       }
-       memset(&a->d, 0x00, sizeof(DESCRIPTOR));
-}
-
-/* ------------------------------------------------------------------------
-   Cleanup, but NOT delete this entity
-   ------------------------------------------------------------------------ */
-static void cleanup_entity(divas_um_idi_entity_t *e)
-{
-       e->os_ref = NULL;
-       e->status = 0;
-       e->adapter = NULL;
-       e->e.Id = 0;
-       e->rc_count = 0;
-
-       e->status |= DIVA_UM_IDI_REMOVED;
-       e->status |= DIVA_UM_IDI_REMOVE_PENDING;
-
-       diva_data_q_finit(&e->data);
-       diva_data_q_finit(&e->rc);
-}
-
-
-/* ------------------------------------------------------------------------
-   Create ENTITY, link it to the adapter and remove pointer to entity
-   ------------------------------------------------------------------------ */
-void *divas_um_idi_create_entity(dword adapter_nr, void *file)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       diva_os_spin_lock_magic_t old_irql;
-
-       if ((e = (divas_um_idi_entity_t *) diva_os_malloc(0, sizeof(*e)))) {
-               memset(e, 0x00, sizeof(*e));
-               if (!
-                   (e->os_context =
-                    diva_os_malloc(0, diva_os_get_context_size()))) {
-                       DBG_LOG(("E(%08x) no memory for os context", e));
-                       diva_os_free(0, e);
-                       return NULL;
-               }
-               memset(e->os_context, 0x00, diva_os_get_context_size());
-
-               if ((diva_data_q_init(&e->data, 2048 + 512, 16))) {
-                       diva_os_free(0, e->os_context);
-                       diva_os_free(0, e);
-                       return NULL;
-               }
-               if ((diva_data_q_init(&e->rc, sizeof(diva_um_idi_ind_hdr_t), 2))) {
-                       diva_data_q_finit(&e->data);
-                       diva_os_free(0, e->os_context);
-                       diva_os_free(0, e);
-                       return NULL;
-               }
-
-               diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_entity");
-               /*
-                 Look for Adapter requested
-               */
-               if (!(a = diva_um_idi_find_adapter(adapter_nr))) {
-                       /*
-                         No adapter was found, or this adapter was removed
-                       */
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
-
-                       DBG_LOG(("A: no adapter(%ld)", adapter_nr));
-
-                       cleanup_entity(e);
-                       diva_os_free(0, e->os_context);
-                       diva_os_free(0, e);
-
-                       return NULL;
-               }
-
-               e->os_ref = file;       /* link to os handle */
-               e->adapter = a; /* link to adapter   */
-
-               list_add_tail(&e->link, &a->entity_q);  /* link from adapter */
-
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
-
-               DBG_LOG(("A(%ld), create E(%08x)", adapter_nr, e));
-       }
-
-       return (e);
-}
-
-/* ------------------------------------------------------------------------
-   Unlink entity and free memory
-   ------------------------------------------------------------------------ */
-int divas_um_idi_delete_entity(int adapter_nr, void *entity)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       diva_os_spin_lock_magic_t old_irql;
-
-       if (!(e = (divas_um_idi_entity_t *) entity))
-               return (-1);
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "delete_entity");
-       if ((a = e->adapter)) {
-               list_del(&e->link);
-       }
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "delete_entity");
-
-       diva_um_idi_stop_wdog(entity);
-       cleanup_entity(e);
-       diva_os_free(0, e->os_context);
-       memset(e, 0x00, sizeof(*e));
-
-       DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e));
-       diva_os_free(0, e);
-
-       return (0);
-}
-
-/* --------------------------------------------------------------------------
-   Called by application to read data from IDI
-   -------------------------------------------------------------------------- */
-int diva_um_idi_read(void *entity,
-                    void *os_handle,
-                    void *dst,
-                    int max_length, divas_um_idi_copy_to_user_fn_t cp_fn)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       const void *data;
-       int length, ret = 0;
-       diva_um_idi_data_queue_t *q;
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "read");
-
-       e = (divas_um_idi_entity_t *) entity;
-       if (!e || (!(a = e->adapter)) ||
-           (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
-           (e->status & DIVA_UM_IDI_REMOVED) ||
-           (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
-               DBG_ERR(("E(%08x) read failed - adapter removed", e))
-                       return (-1);
-       }
-
-       DBG_TRC(("A(%d) E(%08x) read(%d)", a->adapter_nr, e, max_length));
-
-       /*
-         Try to read return code first
-       */
-       data = diva_data_q_get_segment4read(&e->rc);
-       q = &e->rc;
-
-       /*
-         No return codes available, read indications now
-       */
-       if (!data) {
-               if (!(e->status & DIVA_UM_IDI_RC_PENDING)) {
-                       DBG_TRC(("A(%d) E(%08x) read data", a->adapter_nr, e));
-                       data = diva_data_q_get_segment4read(&e->data);
-                       q = &e->data;
-               }
-       } else {
-               e->status &= ~DIVA_UM_IDI_RC_PENDING;
-               DBG_TRC(("A(%d) E(%08x) read rc", a->adapter_nr, e));
-       }
-
-       if (data) {
-               if ((length = diva_data_q_get_segment_length(q)) >
-                   max_length) {
-                       /*
-                         Not enough space to read message
-                       */
-                       DBG_ERR(("A: A(%d) E(%08x) read small buffer",
-                                a->adapter_nr, e, ret));
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql,
-                                               "read");
-                       return (-2);
-               }
-               /*
-                 Copy it to user, this function does access ONLY locked an verified
-                 memory, also we can access it witch spin lock held
-               */
-
-               if ((ret = (*cp_fn) (os_handle, dst, data, length)) >= 0) {
-                       /*
-                         Acknowledge only if read was successful
-                       */
-                       diva_data_q_ack_segment4read(q);
-               }
-       }
-
-
-       DBG_TRC(("A(%d) E(%08x) read=%d", a->adapter_nr, e, ret));
-
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
-
-       return (ret);
-}
-
-
-int diva_um_idi_write(void *entity,
-                     void *os_handle,
-                     const void *src,
-                     int length, divas_um_idi_copy_from_user_fn_t cp_fn)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       diva_um_idi_req_hdr_t *req;
-       void *data;
-       int ret = 0;
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "write");
-
-       e = (divas_um_idi_entity_t *) entity;
-       if (!e || (!(a = e->adapter)) ||
-           (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
-           (e->status & DIVA_UM_IDI_REMOVED) ||
-           (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-               DBG_ERR(("E(%08x) write failed - adapter removed", e))
-                       return (-1);
-       }
-
-       DBG_TRC(("A(%d) E(%08x) write(%d)", a->adapter_nr, e, length));
-
-       if ((length < sizeof(*req)) || (length > sizeof(e->buffer))) {
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-               return (-2);
-       }
-
-       if (e->status & DIVA_UM_IDI_RC_PENDING) {
-               DBG_ERR(("A: A(%d) E(%08x) rc pending", a->adapter_nr, e));
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-               return (-1);    /* should wait for RC code first */
-       }
-
-       /*
-         Copy function does access only locked verified memory,
-         also it can be called with spin lock held
-       */
-       if ((ret = (*cp_fn) (os_handle, e->buffer, src, length)) < 0) {
-               DBG_TRC(("A: A(%d) E(%08x) write error=%d", a->adapter_nr,
-                        e, ret));
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-               return (ret);
-       }
-
-       req = (diva_um_idi_req_hdr_t *)&e->buffer[0];
-
-       switch (req->type) {
-       case DIVA_UM_IDI_GET_FEATURES:{
-               DBG_LOG(("A(%d) get_features", a->adapter_nr));
-               if (!(data =
-                     diva_data_q_get_segment4write(&e->data))) {
-                       DBG_ERR(("A(%d) get_features, no free buffer",
-                                a->adapter_nr));
-                       diva_os_leave_spin_lock(&adapter_lock,
-                                               &old_irql,
-                                               "write");
-                       return (0);
-               }
-               diva_user_mode_idi_adapter_features(a, &(((diva_um_idi_ind_hdr_t
-                                                          *) data)->hdr.features));
-               ((diva_um_idi_ind_hdr_t *) data)->type =
-                       DIVA_UM_IDI_IND_FEATURES;
-               ((diva_um_idi_ind_hdr_t *) data)->data_length = 0;
-               diva_data_q_ack_segment4write(&e->data,
-                                             sizeof(diva_um_idi_ind_hdr_t));
-
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-
-               diva_os_wakeup_read(e->os_context);
-       }
-               break;
-
-       case DIVA_UM_IDI_REQ:
-       case DIVA_UM_IDI_REQ_MAN:
-       case DIVA_UM_IDI_REQ_SIG:
-       case DIVA_UM_IDI_REQ_NET:
-               DBG_TRC(("A(%d) REQ(%02d)-(%02d)-(%08x)", a->adapter_nr,
-                        req->Req, req->ReqCh,
-                        req->type & DIVA_UM_IDI_REQ_TYPE_MASK));
-               switch (process_idi_request(e, req)) {
-               case -1:
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-                       return (-1);
-               case -2:
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-                       diva_os_wakeup_read(e->os_context);
-                       break;
-               default:
-                       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-                       break;
-               }
-               break;
-
-       default:
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
-               return (-1);
-       }
-
-       DBG_TRC(("A(%d) E(%08x) write=%d", a->adapter_nr, e, ret));
-
-       return (ret);
-}
-
-/* --------------------------------------------------------------------------
-   CALLBACK FROM XDI
-   -------------------------------------------------------------------------- */
-static void diva_um_idi_xdi_callback(ENTITY *entity)
-{
-       divas_um_idi_entity_t *e = DIVAS_CONTAINING_RECORD(entity,
-                                                          divas_um_idi_entity_t,
-                                                          e);
-       diva_os_spin_lock_magic_t old_irql;
-       int call_wakeup = 0;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
-
-       if (e->e.complete == 255) {
-               if (!(e->status & DIVA_UM_IDI_REMOVE_PENDING)) {
-                       diva_um_idi_stop_wdog(e);
-               }
-               if ((call_wakeup = process_idi_rc(e, e->e.Rc))) {
-                       if (e->rc_count) {
-                               e->rc_count--;
-                       }
-               }
-               e->e.Rc = 0;
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
-
-               if (call_wakeup) {
-                       diva_os_wakeup_read(e->os_context);
-                       diva_os_wakeup_close(e->os_context);
-               }
-       } else {
-               if (e->status & DIVA_UM_IDI_REMOVE_PENDING) {
-                       e->e.RNum = 0;
-                       e->e.RNR = 2;
-               } else {
-                       call_wakeup = process_idi_ind(e, e->e.Ind);
-               }
-               e->e.Ind = 0;
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
-               if (call_wakeup) {
-                       diva_os_wakeup_read(e->os_context);
-               }
-       }
-}
-
-static int process_idi_request(divas_um_idi_entity_t *e,
-                              const diva_um_idi_req_hdr_t *req)
-{
-       int assign = 0;
-       byte Req = (byte) req->Req;
-       dword type = req->type & DIVA_UM_IDI_REQ_TYPE_MASK;
-
-       if (!e->e.Id || !e->e.callback) {       /* not assigned */
-               if (Req != ASSIGN) {
-                       DBG_ERR(("A: A(%d) E(%08x) not assigned",
-                                e->adapter->adapter_nr, e));
-                       return (-1);    /* NOT ASSIGNED */
-               } else {
-                       switch (type) {
-                       case DIVA_UM_IDI_REQ_TYPE_MAN:
-                               e->e.Id = MAN_ID;
-                               DBG_TRC(("A(%d) E(%08x) assign MAN",
-                                        e->adapter->adapter_nr, e));
-                               break;
-
-                       case DIVA_UM_IDI_REQ_TYPE_SIG:
-                               e->e.Id = DSIG_ID;
-                               DBG_TRC(("A(%d) E(%08x) assign SIG",
-                                        e->adapter->adapter_nr, e));
-                               break;
-
-                       case DIVA_UM_IDI_REQ_TYPE_NET:
-                               e->e.Id = NL_ID;
-                               DBG_TRC(("A(%d) E(%08x) assign NET",
-                                        e->adapter->adapter_nr, e));
-                               break;
-
-                       default:
-                               DBG_ERR(("A: A(%d) E(%08x) unknown type=%08x",
-                                        e->adapter->adapter_nr, e,
-                                        type));
-                               return (-1);
-                       }
-               }
-               e->e.XNum = 1;
-               e->e.RNum = 1;
-               e->e.callback = diva_um_idi_xdi_callback;
-               e->e.X = &e->XData;
-               e->e.R = &e->RData;
-               assign = 1;
-       }
-       e->status |= DIVA_UM_IDI_RC_PENDING;
-       e->e.Req = Req;
-       e->e.ReqCh = (byte) req->ReqCh;
-       e->e.X->PLength = (word) req->data_length;
-       e->e.X->P = (byte *)&req[1];    /* Our buffer is safe */
-
-       DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
-                e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
-                e->e.ReqCh, e->e.X->PLength));
-
-       e->rc_count++;
-
-       if (e->adapter && e->adapter->d.request) {
-               diva_um_idi_start_wdog(e);
-               (*(e->adapter->d.request)) (&e->e);
-       }
-
-       if (assign) {
-               if (e->e.Rc == OUT_OF_RESOURCES) {
-                       /*
-                         XDI has no entities more, call was not forwarded to the card,
-                         no callback will be scheduled
-                       */
-                       DBG_ERR(("A: A(%d) E(%08x) XDI out of entities",
-                                e->adapter->adapter_nr, e));
-
-                       e->e.Id = 0;
-                       e->e.ReqCh = 0;
-                       e->e.RcCh = 0;
-                       e->e.Ind = 0;
-                       e->e.IndCh = 0;
-                       e->e.XNum = 0;
-                       e->e.RNum = 0;
-                       e->e.callback = NULL;
-                       e->e.X = NULL;
-                       e->e.R = NULL;
-                       write_return_code(e, ASSIGN_RC | OUT_OF_RESOURCES);
-                       return (-2);
-               } else {
-                       e->status |= DIVA_UM_IDI_ASSIGN_PENDING;
-               }
-       }
-
-       return (0);
-}
-
-static int process_idi_rc(divas_um_idi_entity_t *e, byte rc)
-{
-       DBG_TRC(("A(%d) E(%08x) rc(%02x-%02x-%02x)",
-                e->adapter->adapter_nr, e, e->e.Id, rc, e->e.RcCh));
-
-       if (e->status & DIVA_UM_IDI_ASSIGN_PENDING) {
-               e->status &= ~DIVA_UM_IDI_ASSIGN_PENDING;
-               if (rc != ASSIGN_OK) {
-                       DBG_ERR(("A: A(%d) E(%08x) ASSIGN failed",
-                                e->adapter->adapter_nr, e));
-                       e->e.callback = NULL;
-                       e->e.Id = 0;
-                       e->e.Req = 0;
-                       e->e.ReqCh = 0;
-                       e->e.Rc = 0;
-                       e->e.RcCh = 0;
-                       e->e.Ind = 0;
-                       e->e.IndCh = 0;
-                       e->e.X = NULL;
-                       e->e.R = NULL;
-                       e->e.XNum = 0;
-                       e->e.RNum = 0;
-               }
-       }
-       if ((e->e.Req == REMOVE) && e->e.Id && (rc == 0xff)) {
-               DBG_ERR(("A: A(%d) E(%08x)  discard OK in REMOVE",
-                        e->adapter->adapter_nr, e));
-               return (0);     /* let us do it in the driver */
-       }
-       if ((e->e.Req == REMOVE) && (!e->e.Id)) {       /* REMOVE COMPLETE */
-               e->e.callback = NULL;
-               e->e.Id = 0;
-               e->e.Req = 0;
-               e->e.ReqCh = 0;
-               e->e.Rc = 0;
-               e->e.RcCh = 0;
-               e->e.Ind = 0;
-               e->e.IndCh = 0;
-               e->e.X = NULL;
-               e->e.R = NULL;
-               e->e.XNum = 0;
-               e->e.RNum = 0;
-               e->rc_count = 0;
-       }
-       if ((e->e.Req == REMOVE) && (rc != 0xff)) {     /* REMOVE FAILED */
-               DBG_ERR(("A: A(%d) E(%08x)  REMOVE FAILED",
-                        e->adapter->adapter_nr, e));
-       }
-       write_return_code(e, rc);
-
-       return (1);
-}
-
-static int process_idi_ind(divas_um_idi_entity_t *e, byte ind)
-{
-       int do_wakeup = 0;
-
-       if (e->e.complete != 0x02) {
-               diva_um_idi_ind_hdr_t *pind =
-                       (diva_um_idi_ind_hdr_t *)
-                       diva_data_q_get_segment4write(&e->data);
-               if (pind) {
-                       e->e.RNum = 1;
-                       e->e.R->P = (byte *)&pind[1];
-                       e->e.R->PLength =
-                               (word) (diva_data_q_get_max_length(&e->data) -
-                                       sizeof(*pind));
-                       DBG_TRC(("A(%d) E(%08x) ind_1(%02x-%02x-%02x)-[%d-%d]",
-                                e->adapter->adapter_nr, e, e->e.Id, ind,
-                                e->e.IndCh, e->e.RLength,
-                                e->e.R->PLength));
-
-               } else {
-                       DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-RNR",
-                                e->adapter->adapter_nr, e, e->e.Id, ind,
-                                e->e.IndCh));
-                       e->e.RNum = 0;
-                       e->e.RNR = 1;
-                       do_wakeup = 1;
-               }
-       } else {
-               diva_um_idi_ind_hdr_t *pind =
-                       (diva_um_idi_ind_hdr_t *) (e->e.R->P);
-
-               DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-[%d]",
-                        e->adapter->adapter_nr, e, e->e.Id, ind,
-                        e->e.IndCh, e->e.R->PLength));
-
-               pind--;
-               pind->type = DIVA_UM_IDI_IND;
-               pind->hdr.ind.Ind = ind;
-               pind->hdr.ind.IndCh = e->e.IndCh;
-               pind->data_length = e->e.R->PLength;
-               diva_data_q_ack_segment4write(&e->data,
-                                             (int) (sizeof(*pind) +
-                                                    e->e.R->PLength));
-               do_wakeup = 1;
-       }
-
-       if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
-               do_wakeup = 0;
-       }
-
-       return (do_wakeup);
-}
-
-/* --------------------------------------------------------------------------
-   Write return code to the return code queue of entity
-   -------------------------------------------------------------------------- */
-static int write_return_code(divas_um_idi_entity_t *e, byte rc)
-{
-       diva_um_idi_ind_hdr_t *prc;
-
-       if (!(prc =
-             (diva_um_idi_ind_hdr_t *) diva_data_q_get_segment4write(&e->rc)))
-       {
-               DBG_ERR(("A: A(%d) E(%08x) rc(%02x) lost",
-                        e->adapter->adapter_nr, e, rc));
-               e->status &= ~DIVA_UM_IDI_RC_PENDING;
-               return (-1);
-       }
-
-       prc->type = DIVA_UM_IDI_IND_RC;
-       prc->hdr.rc.Rc = rc;
-       prc->hdr.rc.RcCh = e->e.RcCh;
-       prc->data_length = 0;
-       diva_data_q_ack_segment4write(&e->rc, sizeof(*prc));
-
-       return (0);
-}
-
-/* --------------------------------------------------------------------------
-   Return amount of entries that can be bead from this entity or
-   -1 if adapter was removed
-   -------------------------------------------------------------------------- */
-int diva_user_mode_idi_ind_ready(void *entity, void *os_handle)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       diva_os_spin_lock_magic_t old_irql;
-       int ret;
-
-       if (!entity)
-               return (-1);
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "ind_ready");
-       e = (divas_um_idi_entity_t *) entity;
-       a = e->adapter;
-
-       if ((!a) || (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
-               /*
-                 Adapter was unloaded
-               */
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
-               return (-1);    /* adapter was removed */
-       }
-       if (e->status & DIVA_UM_IDI_REMOVED) {
-               /*
-                 entity was removed as result of adapter removal
-                 user should assign this entity again
-               */
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
-               return (-1);
-       }
-
-       ret = e->rc.count + e->data.count;
-
-       if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
-               ret = 0;
-       }
-
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
-
-       return (ret);
-}
-
-void *diva_um_id_get_os_context(void *entity)
-{
-       return (((divas_um_idi_entity_t *) entity)->os_context);
-}
-
-int divas_um_idi_entity_assigned(void *entity)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       int ret;
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "assigned?");
-
-
-       e = (divas_um_idi_entity_t *) entity;
-       if (!e || (!(a = e->adapter)) ||
-           (e->status & DIVA_UM_IDI_REMOVED) ||
-           (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
-               return (0);
-       }
-
-       e->status |= DIVA_UM_IDI_REMOVE_PENDING;
-
-       ret = (e->e.Id || e->rc_count
-              || (e->status & DIVA_UM_IDI_ASSIGN_PENDING));
-
-       DBG_TRC(("Id:%02x, rc_count:%d, status:%08x", e->e.Id, e->rc_count,
-                e->status))
-
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
-
-       return (ret);
-}
-
-int divas_um_idi_entity_start_remove(void *entity)
-{
-       divas_um_idi_entity_t *e;
-       diva_um_idi_adapter_t *a;
-       diva_os_spin_lock_magic_t old_irql;
-
-       diva_os_enter_spin_lock(&adapter_lock, &old_irql, "start_remove");
-
-       e = (divas_um_idi_entity_t *) entity;
-       if (!e || (!(a = e->adapter)) ||
-           (e->status & DIVA_UM_IDI_REMOVED) ||
-           (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
-               return (0);
-       }
-
-       if (e->rc_count) {
-               /*
-                 Entity BUSY
-               */
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
-               return (1);
-       }
-
-       if (!e->e.Id) {
-               /*
-                 Remove request was already pending, and arrived now
-               */
-               diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
-               return (0);     /* REMOVE was pending */
-       }
-
-       /*
-         Now send remove request
-       */
-       e->e.Req = REMOVE;
-       e->e.ReqCh = 0;
-
-       e->rc_count++;
-
-       DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
-                e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
-                e->e.ReqCh, e->e.X->PLength));
-
-       if (a->d.request)
-               (*(a->d.request)) (&e->e);
-
-       diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
-
-       return (0);
-}
diff --git a/drivers/isdn/hardware/eicon/um_idi.h b/drivers/isdn/hardware/eicon/um_idi.h
deleted file mode 100644 (file)
index 9aedd9e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: um_idi.h,v 1.6 2004/03/21 17:26:01 armin Exp $ */
-
-#ifndef __DIVA_USER_MODE_IDI_CORE_H__
-#define __DIVA_USER_MODE_IDI_CORE_H__
-
-
-/*
-  interface between UM IDI core and OS dependent part
-*/
-int diva_user_mode_idi_init(void);
-void diva_user_mode_idi_finit(void);
-void *divas_um_idi_create_entity(dword adapter_nr, void *file);
-int divas_um_idi_delete_entity(int adapter_nr, void *entity);
-
-typedef int (*divas_um_idi_copy_to_user_fn_t) (void *os_handle,
-                                              void *dst,
-                                              const void *src,
-                                              int length);
-typedef int (*divas_um_idi_copy_from_user_fn_t) (void *os_handle,
-                                                void *dst,
-                                                const void *src,
-                                                int length);
-
-int diva_um_idi_read(void *entity,
-                    void *os_handle,
-                    void *dst,
-                    int max_length, divas_um_idi_copy_to_user_fn_t cp_fn);
-
-int diva_um_idi_write(void *entity,
-                     void *os_handle,
-                     const void *src,
-                     int length, divas_um_idi_copy_from_user_fn_t cp_fn);
-
-int diva_user_mode_idi_ind_ready(void *entity, void *os_handle);
-void *diva_um_id_get_os_context(void *entity);
-int diva_os_get_context_size(void);
-int divas_um_idi_entity_assigned(void *entity);
-int divas_um_idi_entity_start_remove(void *entity);
-
-void diva_um_idi_start_wdog(void *entity);
-void diva_um_idi_stop_wdog(void *entity);
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/um_xdi.h b/drivers/isdn/hardware/eicon/um_xdi.h
deleted file mode 100644 (file)
index 1f37aa4..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: um_xdi.h,v 1.1.2.2 2002/10/02 14:38:38 armin Exp $ */
-
-#ifndef __DIVA_USER_MODE_XDI_H__
-#define __DIVA_USER_MODE_XDI_H__
-
-/*
-  Contains declaratiom of structures shared between application
-  and user mode idi driver
-*/
-
-typedef struct _diva_um_idi_adapter_features {
-       dword type;
-       dword features;
-       dword channels;
-       dword serial_number;
-       char name[128];
-} diva_um_idi_adapter_features_t;
-
-#define DIVA_UM_IDI_REQ_MASK                   0x0000FFFF
-#define DIVA_UM_IDI_REQ_TYPE_MASK              (~(DIVA_UM_IDI_REQ_MASK))
-#define DIVA_UM_IDI_GET_FEATURES               1       /* trigger features indication */
-#define DIVA_UM_IDI_REQ                                2
-#define DIVA_UM_IDI_REQ_TYPE_MAN               0x10000000
-#define DIVA_UM_IDI_REQ_TYPE_SIG               0x20000000
-#define DIVA_UM_IDI_REQ_TYPE_NET               0x30000000
-#define DIVA_UM_IDI_REQ_MAN                    (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_MAN)
-#define DIVA_UM_IDI_REQ_SIG                    (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_SIG)
-#define DIVA_UM_IDI_REQ_NET                    (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_NET)
-/*
-  data_length  bytes will follow this structure
-*/
-typedef struct _diva_um_idi_req_hdr {
-       dword type;
-       dword Req;
-       dword ReqCh;
-       dword data_length;
-} diva_um_idi_req_hdr_t;
-
-typedef struct _diva_um_idi_ind_parameters {
-       dword Ind;
-       dword IndCh;
-} diva_um_idi_ind_parameters_t;
-
-typedef struct _diva_um_idi_rc_parameters {
-       dword Rc;
-       dword RcCh;
-} diva_um_idi_rc_parameters_t;
-
-typedef union _diva_um_idi_ind {
-       diva_um_idi_adapter_features_t features;
-       diva_um_idi_ind_parameters_t ind;
-       diva_um_idi_rc_parameters_t rc;
-} diva_um_idi_ind_t;
-
-#define DIVA_UM_IDI_IND_FEATURES  1    /* features indication */
-#define DIVA_UM_IDI_IND           2
-#define DIVA_UM_IDI_IND_RC        3
-/*
-  data_length bytes of data follow
-  this structure
-*/
-typedef struct _diva_um_idi_ind_hdr {
-       dword type;
-       diva_um_idi_ind_t hdr;
-       dword data_length;
-} diva_um_idi_ind_hdr_t;
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_adapter.h b/drivers/isdn/hardware/eicon/xdi_adapter.h
deleted file mode 100644 (file)
index b036e21..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: xdi_adapter.h,v 1.7 2004/03/21 17:26:01 armin Exp $ */
-
-#ifndef __DIVA_OS_XDI_ADAPTER_H__
-#define __DIVA_OS_XDI_ADAPTER_H__
-
-#define DIVAS_XDI_ADAPTER_BUS_PCI  0
-#define DIVAS_XDI_ADAPTER_BUS_ISA  1
-
-typedef struct _divas_pci_card_resources {
-       byte bus;
-       byte func;
-       void *hdev;
-
-       dword bar[8];           /* contains context of appropriate BAR Register */
-       void __iomem *addr[8];          /* same bar, but mapped into memory */
-       dword length[8];        /* bar length */
-       int mem_type_id[MAX_MEM_TYPE];
-       unsigned int qoffset;
-       byte irq;
-} divas_pci_card_resources_t;
-
-typedef union _divas_card_resources {
-       divas_pci_card_resources_t pci;
-} divas_card_resources_t;
-
-struct _diva_os_xdi_adapter;
-typedef int (*diva_init_card_proc_t)(struct _diva_os_xdi_adapter *a);
-typedef int (*diva_cmd_card_proc_t)(struct _diva_os_xdi_adapter *a,
-                                   diva_xdi_um_cfg_cmd_t *data,
-                                   int length);
-typedef void (*diva_xdi_clear_interrupts_proc_t)(struct
-                                                _diva_os_xdi_adapter *);
-
-#define DIVA_XDI_MBOX_BUSY                     1
-#define DIVA_XDI_MBOX_WAIT_XLOG        2
-
-typedef struct _xdi_mbox_t {
-       dword status;
-       diva_xdi_um_cfg_cmd_data_t cmd_data;
-       dword data_length;
-       void *data;
-} xdi_mbox_t;
-
-typedef struct _diva_os_idi_adapter_interface {
-       diva_init_card_proc_t cleanup_adapter_proc;
-       diva_cmd_card_proc_t cmd_proc;
-} diva_os_idi_adapter_interface_t;
-
-typedef struct _diva_os_xdi_adapter {
-       struct list_head link;
-       int CardIndex;
-       int CardOrdinal;
-       int controller;         /* number of this controller */
-       int Bus;                /* PCI, ISA, ... */
-       divas_card_resources_t resources;
-       char port_name[24];
-       ISDN_ADAPTER xdi_adapter;
-       xdi_mbox_t xdi_mbox;
-       diva_os_idi_adapter_interface_t interface;
-       struct _diva_os_xdi_adapter *slave_adapters[3];
-       void *slave_list;
-       void *proc_adapter_dir; /* adapterX proc entry */
-       void *proc_info;        /* info proc entry     */
-       void *proc_grp_opt;     /* group_optimization  */
-       void *proc_d_l1_down;   /* dynamic_l1_down     */
-       volatile diva_xdi_clear_interrupts_proc_t clear_interrupts_proc;
-       dword dsp_mask;
-} diva_os_xdi_adapter_t;
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_msg.h b/drivers/isdn/hardware/eicon/xdi_msg.h
deleted file mode 100644 (file)
index 0646079..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: xdi_msg.h,v 1.1.2.2 2001/02/16 08:40:36 armin Exp $ */
-
-#ifndef __DIVA_XDI_UM_CFG_MESSAGE_H__
-#define __DIVA_XDI_UM_CFG_MESSAGE_H__
-
-/*
-  Definition of messages used to communicate between
-  XDI device driver and user mode configuration utility
-*/
-
-/*
-  As acknowledge one DWORD - card ordinal will be read from the card
-*/
-#define DIVA_XDI_UM_CMD_GET_CARD_ORDINAL       0
-
-/*
-  no acknowledge will be generated, memory block will be written in the
-  memory at given offset
-*/
-#define DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK      1
-
-/*
-  no acknowledge will be genatated, FPGA will be programmed
-*/
-#define DIVA_XDI_UM_CMD_WRITE_FPGA                             2
-
-/*
-  As acknowledge block of SDRAM will be read in the user buffer
-*/
-#define DIVA_XDI_UM_CMD_READ_SDRAM                             3
-
-/*
-  As acknowledge dword with serial number will be read in the user buffer
-*/
-#define DIVA_XDI_UM_CMD_GET_SERIAL_NR                  4
-
-/*
-  As acknowledge struct consisting from 9 dwords with PCI info.
-  dword[0...7] = 8 PCI BARS
-  dword[9]              = IRQ
-*/
-#define DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG      5
-
-/*
-  Reset of the board + activation of primary
-  boot loader
-*/
-#define DIVA_XDI_UM_CMD_RESET_ADAPTER                  6
-
-/*
-  Called after code download to start adapter
-  at specified address
-  Start does set new set of features due to fact that we not know
-  if protocol features have changed
-*/
-#define DIVA_XDI_UM_CMD_START_ADAPTER                  7
-
-/*
-  Stop adapter, called if user
-  wishes to stop adapter without unload
-  of the driver, to reload adapter with
-  different protocol
-*/
-#define DIVA_XDI_UM_CMD_STOP_ADAPTER                   8
-
-/*
-  Get state of current adapter
-  Acknowledge is one dword with following values:
-  0 - adapter ready for download
-  1 - adapter running
-  2 - adapter dead
-  3 - out of service, driver should be restarted or hardware problem
-*/
-#define DIVA_XDI_UM_CMD_GET_CARD_STATE         9
-
-/*
-  Reads XLOG entry from the card
-*/
-#define DIVA_XDI_UM_CMD_READ_XLOG_ENTRY                10
-
-/*
-  Set untranslated protocol code features
-*/
-#define DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES  11
-
-typedef struct _diva_xdi_um_cfg_cmd_data_set_features {
-       dword features;
-} diva_xdi_um_cfg_cmd_data_set_features_t;
-
-typedef struct _diva_xdi_um_cfg_cmd_data_start {
-       dword offset;
-       dword features;
-} diva_xdi_um_cfg_cmd_data_start_t;
-
-typedef struct _diva_xdi_um_cfg_cmd_data_write_sdram {
-       dword ram_number;
-       dword offset;
-       dword length;
-} diva_xdi_um_cfg_cmd_data_write_sdram_t;
-
-typedef struct _diva_xdi_um_cfg_cmd_data_write_fpga {
-       dword fpga_number;
-       dword image_length;
-} diva_xdi_um_cfg_cmd_data_write_fpga_t;
-
-typedef struct _diva_xdi_um_cfg_cmd_data_read_sdram {
-       dword ram_number;
-       dword offset;
-       dword length;
-} diva_xdi_um_cfg_cmd_data_read_sdram_t;
-
-typedef union _diva_xdi_um_cfg_cmd_data {
-       diva_xdi_um_cfg_cmd_data_write_sdram_t write_sdram;
-       diva_xdi_um_cfg_cmd_data_write_fpga_t write_fpga;
-       diva_xdi_um_cfg_cmd_data_read_sdram_t read_sdram;
-       diva_xdi_um_cfg_cmd_data_start_t start;
-       diva_xdi_um_cfg_cmd_data_set_features_t features;
-} diva_xdi_um_cfg_cmd_data_t;
-
-typedef struct _diva_xdi_um_cfg_cmd {
-       dword adapter;          /* Adapter number 1...N */
-       dword command;
-       diva_xdi_um_cfg_cmd_data_t command_data;
-       dword data_length;      /* Plain binary data will follow */
-} diva_xdi_um_cfg_cmd_t;
-
-#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_vers.h b/drivers/isdn/hardware/eicon/xdi_vers.h
deleted file mode 100644 (file)
index b3479e5..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-
-/*
- *
- Copyright (c) Eicon Networks, 2002.
- *
- This source file is supplied for the use with
- Eicon Networks range of DIVA Server Adapters.
- *
- Eicon File Revision :    2.1
- *
- 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, or (at your option)
- any later version.
- *
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
- implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- *
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-static char diva_xdi_common_code_build[] = "102-52";
index 5acf6ab..6f60ace 100644 (file)
@@ -52,10 +52,7 @@ static const struct w6692map  w6692_map[] =
        {W6692_USR, "USR W6692"}
 };
 
-#ifndef PCI_VENDOR_ID_USR
-#define PCI_VENDOR_ID_USR      0x16ec
 #define PCI_DEVICE_ID_USR_6692 0x3409
-#endif
 
 struct w6692_ch {
        struct bchannel         bch;
index ea0e4c6..5b719b5 100644 (file)
@@ -274,7 +274,7 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
        u_char *ptr, *ptr1, new_f2;
        struct sk_buff *skb;
        struct IsdnCardState *cs = bcs->cs;
-       int total, maxlen, new_z2;
+       int maxlen, new_z2;
        z_type *zp;
 
        if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
@@ -297,7 +297,6 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
        } else if (!(skb = dev_alloc_skb(count - 3)))
                printk(KERN_WARNING "HFCPCI: receive out of memory\n");
        else {
-               total = count;
                count -= 3;
                ptr = skb_put(skb, count);
 
index 3633202..6b212c8 100644 (file)
@@ -129,6 +129,16 @@ static u64 vop_get_features(struct virtio_device *vdev)
        return features;
 }
 
+static void vop_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on virtio_vop for now,
+        * because virtio_vop uses vring_new_virtqueue() which
+        * creates virtio rings on preallocated memory.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int vop_finalize_features(struct virtio_device *vdev)
 {
        unsigned int i, bits;
@@ -141,6 +151,9 @@ static int vop_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features. */
        vring_transport_features(vdev);
 
+       /* Give virtio_vop a chance to accept features. */
+       vop_transport_features(vdev);
+
        memset_io(out_features, 0, feature_len);
        bits = min_t(unsigned, feature_len,
                     sizeof(vdev->features)) * 8;
index d037751..6371958 100644 (file)
@@ -397,10 +397,10 @@ config NET_SB1000
 
          At present this driver only compiles as a module, so say M here if
          you have this card. The module will be called sb1000. Then read
-         <file:Documentation/networking/README.sb1000> for information on how
-         to use this module, as it needs special ppp scripts for establishing
-         a connection. Further documentation and the necessary scripts can be
-         found at:
+         <file:Documentation/networking/device_drivers/sb1000.txt> for
+         information on how to use this module, as it needs special ppp
+         scripts for establishing a connection. Further documentation
+         and the necessary scripts can be found at:
 
          <http://www.jacksonville.net/~fventuri/>
          <http://home.adelphia.net/~siglercm/sb1000.html>
index 333387f..6b34dbe 100644 (file)
@@ -1538,7 +1538,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
        slave_dev->flags |= IFF_SLAVE;
 
        /* open the slave since the application closed it */
-       res = dev_open(slave_dev);
+       res = dev_open(slave_dev, extack);
        if (res) {
                netdev_dbg(bond_dev, "Opening slave %s failed\n", slave_dev->name);
                goto err_restore_mac;
index 7cdd0ce..e0f0ad7 100644 (file)
@@ -96,7 +96,7 @@ config CAN_AT91
 
 config CAN_FLEXCAN
        tristate "Support for Freescale FLEXCAN based chips"
-       depends on ARM || PPC
+       depends on OF && HAS_IOMEM
        ---help---
          Say Y here if you want to support for Freescale FlexCAN.
 
index 75ce113..0f36eaf 100644 (file)
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
 
 #define DRV_NAME                       "flexcan"
 
        (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE)
 #define FLEXCAN_ESR_ALL_INT \
        (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \
-        FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT)
+        FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT | \
+        FLEXCAN_ESR_WAK_INT)
 
 /* FLEXCAN interrupt flag register (IFLAG) bits */
 /* Errata ERR005829 step7: Reserve first valid MB */
 #define FLEXCAN_TX_MB_RESERVED_OFF_FIFO                8
 #define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP   0
-#define FLEXCAN_TX_MB                          63
 #define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST      (FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP + 1)
-#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST       (FLEXCAN_TX_MB - 1)
-#define FLEXCAN_IFLAG_MB(x)            BIT(x & 0x1f)
+#define FLEXCAN_IFLAG_MB(x)            BIT((x) & 0x1f)
 #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7)
 #define FLEXCAN_IFLAG_RX_FIFO_WARN     BIT(6)
 #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE        BIT(5)
 #define FLEXCAN_QUIRK_USE_OFF_TIMESTAMP        BIT(5) /* Use timestamp based offloading */
 #define FLEXCAN_QUIRK_BROKEN_PERR_STATE        BIT(6) /* No interrupt for error passive */
 #define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN       BIT(7) /* default to BE register access */
+#define FLEXCAN_QUIRK_SETUP_STOP_MODE          BIT(8) /* Setup stop mode to support wakeup */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
        u32 can_ctrl;
        u32 can_id;
-       u32 data[2];
+       u32 data[];
 };
 
 /* Structure of the hardware registers */
@@ -223,7 +225,7 @@ struct flexcan_regs {
        u32 rxfgmask;           /* 0x48 */
        u32 rxfir;              /* 0x4c */
        u32 _reserved3[12];     /* 0x50 */
-       struct flexcan_mb mb[64];       /* 0x80 */
+       u8 mb[2][512];          /* 0x80 */
        /* FIFO-mode:
         *                      MB
         * 0x080...0x08f        0       RX message buffer
@@ -253,12 +255,24 @@ struct flexcan_devtype_data {
        u32 quirks;             /* quirks needed for different IP cores */
 };
 
+struct flexcan_stop_mode {
+       struct regmap *gpr;
+       u8 req_gpr;
+       u8 req_bit;
+       u8 ack_gpr;
+       u8 ack_bit;
+};
+
 struct flexcan_priv {
        struct can_priv can;
        struct can_rx_offload offload;
 
        struct flexcan_regs __iomem *regs;
+       struct flexcan_mb __iomem *tx_mb;
        struct flexcan_mb __iomem *tx_mb_reserved;
+       u8 tx_mb_idx;
+       u8 mb_count;
+       u8 mb_size;
        u32 reg_ctrl_default;
        u32 reg_imask1_default;
        u32 reg_imask2_default;
@@ -267,6 +281,7 @@ struct flexcan_priv {
        struct clk *clk_per;
        const struct flexcan_devtype_data *devtype_data;
        struct regulator *reg_xceiver;
+       struct flexcan_stop_mode stm;
 
        /* Read and Write APIs */
        u32 (*read)(void __iomem *addr);
@@ -290,7 +305,8 @@ static const struct flexcan_devtype_data fsl_imx28_devtype_data = {
 
 static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
        .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
-               FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE,
+               FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
+               FLEXCAN_QUIRK_SETUP_STOP_MODE,
 };
 
 static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
@@ -350,6 +366,68 @@ static inline void flexcan_write_le(u32 val, void __iomem *addr)
        iowrite32(val, addr);
 }
 
+static struct flexcan_mb __iomem *flexcan_get_mb(const struct flexcan_priv *priv,
+                                                u8 mb_index)
+{
+       u8 bank_size;
+       bool bank;
+
+       if (WARN_ON(mb_index >= priv->mb_count))
+               return NULL;
+
+       bank_size = sizeof(priv->regs->mb[0]) / priv->mb_size;
+
+       bank = mb_index >= bank_size;
+       if (bank)
+               mb_index -= bank_size;
+
+       return (struct flexcan_mb __iomem *)
+               (&priv->regs->mb[bank][priv->mb_size * mb_index]);
+}
+
+static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+
+       if (enable)
+               reg_mcr |= FLEXCAN_MCR_WAK_MSK;
+       else
+               reg_mcr &= ~FLEXCAN_MCR_WAK_MSK;
+
+       priv->write(reg_mcr, &regs->mcr);
+}
+
+static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr |= FLEXCAN_MCR_SLF_WAK;
+       priv->write(reg_mcr, &regs->mcr);
+
+       /* enable stop request */
+       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                          1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+}
+
+static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
+{
+       struct flexcan_regs __iomem *regs = priv->regs;
+       u32 reg_mcr;
+
+       /* remove stop request */
+       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                          1 << priv->stm.req_bit, 0);
+
+       reg_mcr = priv->read(&regs->mcr);
+       reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
+       priv->write(reg_mcr, &regs->mcr);
+}
+
 static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
 {
        struct flexcan_regs __iomem *regs = priv->regs;
@@ -512,11 +590,11 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
 static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        const struct flexcan_priv *priv = netdev_priv(dev);
-       struct flexcan_regs __iomem *regs = priv->regs;
        struct can_frame *cf = (struct can_frame *)skb->data;
        u32 can_id;
        u32 data;
        u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16);
+       int i;
 
        if (can_dropped_invalid_skb(dev, skb))
                return NETDEV_TX_OK;
@@ -533,27 +611,23 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
        if (cf->can_id & CAN_RTR_FLAG)
                ctrl |= FLEXCAN_MB_CNT_RTR;
 
-       if (cf->can_dlc > 0) {
-               data = be32_to_cpup((__be32 *)&cf->data[0]);
-               priv->write(data, &regs->mb[FLEXCAN_TX_MB].data[0]);
-       }
-       if (cf->can_dlc > 4) {
-               data = be32_to_cpup((__be32 *)&cf->data[4]);
-               priv->write(data, &regs->mb[FLEXCAN_TX_MB].data[1]);
+       for (i = 0; i < cf->can_dlc; i += sizeof(u32)) {
+               data = be32_to_cpup((__be32 *)&cf->data[i]);
+               priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]);
        }
 
        can_put_echo_skb(skb, dev, 0);
 
-       priv->write(can_id, &regs->mb[FLEXCAN_TX_MB].can_id);
-       priv->write(ctrl, &regs->mb[FLEXCAN_TX_MB].can_ctrl);
+       priv->write(can_id, &priv->tx_mb->can_id);
+       priv->write(ctrl, &priv->tx_mb->can_ctrl);
 
        /* Errata ERR005829 step8:
         * Write twice INACTIVE(0x8) code to first MB.
         */
        priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &priv->tx_mb_reserved->can_ctrl);
+                   &priv->tx_mb_reserved->can_ctrl);
        priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                     &priv->tx_mb_reserved->can_ctrl);
+                   &priv->tx_mb_reserved->can_ctrl);
 
        return NETDEV_TX_OK;
 }
@@ -672,8 +746,11 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
 {
        struct flexcan_priv *priv = rx_offload_to_priv(offload);
        struct flexcan_regs __iomem *regs = priv->regs;
-       struct flexcan_mb __iomem *mb = &regs->mb[n];
+       struct flexcan_mb __iomem *mb;
        u32 reg_ctrl, reg_id, reg_iflag1;
+       int i;
+
+       mb = flexcan_get_mb(priv, n);
 
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
                u32 code;
@@ -714,8 +791,10 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
                cf->can_id |= CAN_RTR_FLAG;
        cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
 
-       *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
-       *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
+       for (i = 0; i < cf->can_dlc; i += sizeof(u32)) {
+               __be32 data = cpu_to_be32(priv->read(&mb->data[i / sizeof(u32)]));
+               *(__be32 *)(cf->data + i) = data;
+       }
 
        /* mark as read */
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
@@ -744,7 +823,7 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
        u32 iflag1, iflag2;
 
        iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default &
-               ~FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB);
+               ~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
        iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default;
 
        return (u64)iflag2 << 32 | iflag1;
@@ -794,8 +873,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        reg_iflag2 = priv->read(&regs->iflag2);
 
        /* transmission complete interrupt */
-       if (reg_iflag2 & FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB)) {
-               u32 reg_ctrl = priv->read(&regs->mb[FLEXCAN_TX_MB].can_ctrl);
+       if (reg_iflag2 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
+               u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl);
 
                handled = IRQ_HANDLED;
                stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
@@ -805,8 +884,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 
                /* after sending a RTR frame MB is in RX mode */
                priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                           &regs->mb[FLEXCAN_TX_MB].can_ctrl);
-               priv->write(FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB), &regs->iflag2);
+                           &priv->tx_mb->can_ctrl);
+               priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag2);
                netif_wake_queue(dev);
        }
 
@@ -821,7 +900,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
        /* state change interrupt or broken error state quirk fix is enabled */
        if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
            (priv->devtype_data->quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE |
-                                          FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
+                                          FLEXCAN_QUIRK_BROKEN_PERR_STATE)))
                flexcan_irq_state(dev, reg_esr);
 
        /* bus error IRQ - handle if bus error reporting is activated */
@@ -919,6 +998,7 @@ static int flexcan_chip_start(struct net_device *dev)
        struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
        int err, i;
+       struct flexcan_mb __iomem *mb;
 
        /* enable module */
        err = flexcan_chip_enable(priv);
@@ -935,11 +1015,9 @@ static int flexcan_chip_start(struct net_device *dev)
        /* MCR
         *
         * enable freeze
-        * enable fifo
         * halt now
         * only supervisor access
         * enable warning int
-        * disable local echo
         * enable individual RX masking
         * choose format C
         * set max mailbox number
@@ -947,14 +1025,37 @@ static int flexcan_chip_start(struct net_device *dev)
        reg_mcr = priv->read(&regs->mcr);
        reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
        reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
-               FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
-               FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_MB);
+               FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_IRMQ | FLEXCAN_MCR_IDAM_C |
+               FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
 
+       /* MCR
+        *
+        * FIFO:
+        * - disable for timestamp mode
+        * - enable for FIFO mode
+        */
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
                reg_mcr &= ~FLEXCAN_MCR_FEN;
        else
                reg_mcr |= FLEXCAN_MCR_FEN;
 
+       /* MCR
+        *
+        * NOTE: In loopback mode, the CAN_MCR[SRXDIS] cannot be
+        *       asserted because this will impede the self reception
+        *       of a transmitted message. This is not documented in
+        *       earlier versions of flexcan block guide.
+        *
+        * Self Reception:
+        * - enable Self Reception for loopback mode
+        *   (by clearing "Self Reception Disable" bit)
+        * - disable for normal operation
+        */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               reg_mcr &= ~FLEXCAN_MCR_SRX_DIS;
+       else
+               reg_mcr |= FLEXCAN_MCR_SRX_DIS;
+
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        priv->write(reg_mcr, &regs->mcr);
 
@@ -999,14 +1100,16 @@ static int flexcan_chip_start(struct net_device *dev)
 
        if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
                for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) {
+                       mb = flexcan_get_mb(priv, i);
                        priv->write(FLEXCAN_MB_CODE_RX_EMPTY,
-                                   &regs->mb[i].can_ctrl);
+                                   &mb->can_ctrl);
                }
        } else {
                /* clear and invalidate unused mailboxes first */
-               for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i <= ARRAY_SIZE(regs->mb); i++) {
+               for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i <= priv->mb_count; i++) {
+                       mb = flexcan_get_mb(priv, i);
                        priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
-                                   &regs->mb[i].can_ctrl);
+                                   &mb->can_ctrl);
                }
        }
 
@@ -1016,7 +1119,7 @@ static int flexcan_chip_start(struct net_device *dev)
 
        /* mark TX mailbox as INACTIVE */
        priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-                   &regs->mb[FLEXCAN_TX_MB].can_ctrl);
+                   &priv->tx_mb->can_ctrl);
 
        /* acceptance mask/acceptance code (accept everything) */
        priv->write(0x0, &regs->rxgmask);
@@ -1027,7 +1130,7 @@ static int flexcan_chip_start(struct net_device *dev)
                priv->write(0x0, &regs->rxfgmask);
 
        /* clear acceptance filters */
-       for (i = 0; i < ARRAY_SIZE(regs->mb); i++)
+       for (i = 0; i < priv->mb_count; i++)
                priv->write(0, &regs->rximr[i]);
 
        /* On Vybrid, disable memory error detection interrupts
@@ -1128,10 +1231,49 @@ static int flexcan_open(struct net_device *dev)
        if (err)
                goto out_close;
 
+       priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
+       priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
+                        (sizeof(priv->regs->mb[1]) / priv->mb_size);
+
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
+               priv->tx_mb_reserved =
+                       flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP);
+       else
+               priv->tx_mb_reserved =
+                       flexcan_get_mb(priv, FLEXCAN_TX_MB_RESERVED_OFF_FIFO);
+       priv->tx_mb_idx = priv->mb_count - 1;
+       priv->tx_mb = flexcan_get_mb(priv, priv->tx_mb_idx);
+
+       priv->reg_imask1_default = 0;
+       priv->reg_imask2_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+
+       priv->offload.mailbox_read = flexcan_mailbox_read;
+
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
+               u64 imask;
+
+               priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
+               priv->offload.mb_last = priv->mb_count - 2;
+
+               imask = GENMASK_ULL(priv->offload.mb_last,
+                                   priv->offload.mb_first);
+               priv->reg_imask1_default |= imask;
+               priv->reg_imask2_default |= imask >> 32;
+
+               err = can_rx_offload_add_timestamp(dev, &priv->offload);
+       } else {
+               priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
+                       FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
+               err = can_rx_offload_add_fifo(dev, &priv->offload,
+                                             FLEXCAN_NAPI_WEIGHT);
+       }
+       if (err)
+               goto out_free_irq;
+
        /* start chip and queuing */
        err = flexcan_chip_start(dev);
        if (err)
-               goto out_free_irq;
+               goto out_offload_del;
 
        can_led_event(dev, CAN_LED_EVENT_OPEN);
 
@@ -1140,6 +1282,8 @@ static int flexcan_open(struct net_device *dev)
 
        return 0;
 
+ out_offload_del:
+       can_rx_offload_del(&priv->offload);
  out_free_irq:
        free_irq(dev->irq, dev);
  out_close:
@@ -1160,6 +1304,7 @@ static int flexcan_close(struct net_device *dev)
        can_rx_offload_disable(&priv->offload);
        flexcan_chip_stop(dev);
 
+       can_rx_offload_del(&priv->offload);
        free_irq(dev->irq, dev);
        clk_disable_unprepare(priv->clk_per);
        clk_disable_unprepare(priv->clk_ipg);
@@ -1260,6 +1405,59 @@ static void unregister_flexcandev(struct net_device *dev)
        unregister_candev(dev);
 }
 
+static int flexcan_setup_stop_mode(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *gpr_np;
+       struct flexcan_priv *priv;
+       phandle phandle;
+       u32 out_val[5];
+       int ret;
+
+       if (!np)
+               return -EINVAL;
+
+       /* stop mode property format is:
+        * <&gpr req_gpr req_bit ack_gpr ack_bit>.
+        */
+       ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val,
+                                        ARRAY_SIZE(out_val));
+       if (ret) {
+               dev_dbg(&pdev->dev, "no stop-mode property\n");
+               return ret;
+       }
+       phandle = *out_val;
+
+       gpr_np = of_find_node_by_phandle(phandle);
+       if (!gpr_np) {
+               dev_dbg(&pdev->dev, "could not find gpr node by phandle\n");
+               return PTR_ERR(gpr_np);
+       }
+
+       priv = netdev_priv(dev);
+       priv->stm.gpr = syscon_node_to_regmap(gpr_np);
+       of_node_put(gpr_np);
+       if (IS_ERR(priv->stm.gpr)) {
+               dev_dbg(&pdev->dev, "could not find gpr regmap\n");
+               return PTR_ERR(priv->stm.gpr);
+       }
+
+       priv->stm.req_gpr = out_val[1];
+       priv->stm.req_bit = out_val[2];
+       priv->stm.ack_gpr = out_val[3];
+       priv->stm.ack_bit = out_val[4];
+
+       dev_dbg(&pdev->dev,
+               "gpr %s req_gpr=0x02%x req_bit=%u ack_gpr=0x02%x ack_bit=%u\n",
+               gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit,
+               priv->stm.ack_gpr, priv->stm.ack_bit);
+
+       device_set_wakeup_capable(&pdev->dev, true);
+
+       return 0;
+}
+
 static const struct of_device_id flexcan_of_match[] = {
        { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
        { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
@@ -1371,35 +1569,6 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->devtype_data = devtype_data;
        priv->reg_xceiver = reg_xceiver;
 
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
-               priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP];
-       else
-               priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO];
-
-       priv->reg_imask1_default = 0;
-       priv->reg_imask2_default = FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB);
-
-       priv->offload.mailbox_read = flexcan_mailbox_read;
-
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-               u64 imask;
-
-               priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
-               priv->offload.mb_last = FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST;
-
-               imask = GENMASK_ULL(priv->offload.mb_last, priv->offload.mb_first);
-               priv->reg_imask1_default |= imask;
-               priv->reg_imask2_default |= imask >> 32;
-
-               err = can_rx_offload_add_timestamp(dev, &priv->offload);
-       } else {
-               priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
-                       FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
-               err = can_rx_offload_add_fifo(dev, &priv->offload, FLEXCAN_NAPI_WEIGHT);
-       }
-       if (err)
-               goto failed_offload;
-
        err = register_flexcandev(dev);
        if (err) {
                dev_err(&pdev->dev, "registering netdev failed\n");
@@ -1408,12 +1577,17 @@ static int flexcan_probe(struct platform_device *pdev)
 
        devm_can_led_init(dev);
 
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE) {
+               err = flexcan_setup_stop_mode(pdev);
+               if (err)
+                       dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
+       }
+
        dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
                 priv->regs, dev->irq);
 
        return 0;
 
- failed_offload:
  failed_register:
        free_candev(dev);
        return err;
@@ -1422,10 +1596,8 @@ static int flexcan_probe(struct platform_device *pdev)
 static int flexcan_remove(struct platform_device *pdev)
 {
        struct net_device *dev = platform_get_drvdata(pdev);
-       struct flexcan_priv *priv = netdev_priv(dev);
 
        unregister_flexcandev(dev);
-       can_rx_offload_del(&priv->offload);
        free_candev(dev);
 
        return 0;
@@ -1438,9 +1610,17 @@ static int __maybe_unused flexcan_suspend(struct device *device)
        int err;
 
        if (netif_running(dev)) {
-               err = flexcan_chip_disable(priv);
-               if (err)
-                       return err;
+               /* if wakeup is enabled, enter stop mode
+                * else enter disabled mode.
+                */
+               if (device_may_wakeup(device)) {
+                       enable_irq_wake(dev->irq);
+                       flexcan_enter_stop_mode(priv);
+               } else {
+                       err = flexcan_chip_disable(priv);
+                       if (err)
+                               return err;
+               }
                netif_stop_queue(dev);
                netif_device_detach(dev);
        }
@@ -1459,14 +1639,45 @@ static int __maybe_unused flexcan_resume(struct device *device)
        if (netif_running(dev)) {
                netif_device_attach(dev);
                netif_start_queue(dev);
-               err = flexcan_chip_enable(priv);
-               if (err)
-                       return err;
+               if (device_may_wakeup(device)) {
+                       disable_irq_wake(dev->irq);
+               } else {
+                       err = flexcan_chip_enable(priv);
+                       if (err)
+                               return err;
+               }
        }
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
+static int __maybe_unused flexcan_noirq_suspend(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device))
+               flexcan_enable_wakeup_irq(priv, true);
+
+       return 0;
+}
+
+static int __maybe_unused flexcan_noirq_resume(struct device *device)
+{
+       struct net_device *dev = dev_get_drvdata(device);
+       struct flexcan_priv *priv = netdev_priv(dev);
+
+       if (netif_running(dev) && device_may_wakeup(device)) {
+               flexcan_enable_wakeup_irq(priv, false);
+               flexcan_exit_stop_mode(priv);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops flexcan_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume)
+};
 
 static struct platform_driver flexcan_driver = {
        .driver = {
index 7b03a3a..bd5a8fc 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
 config CAN_RCAR
        tristate "Renesas R-Car CAN controller"
        depends on ARCH_RENESAS || ARM
index 08de36a..c9185b0 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
 #
 #  Makefile for the Renesas R-Car CAN & CAN FD controller drivers
 #
index 771a460..13e6629 100644 (file)
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /* Renesas R-Car CAN device driver
  *
  * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
  * Copyright (C) 2013 Renesas Solutions Corp.
- *
- * 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/module.h>
index 602c19e..0541000 100644 (file)
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /* Renesas R-Car CAN FD device driver
  *
  * Copyright (C) 2015 Renesas Electronics Corp.
- *
- * 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.
  */
 
 /* The R-Car CAN FD controller can operate in either one of the below two modes
index 1e65cb6..f6dc899 100644 (file)
@@ -88,6 +88,7 @@ config CAN_PLX_PCI
           - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/)
           - IXXAT Automation PC-I 04/PCI card (http://www.ixxat.com/)
           - Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card (http://www.connecttech.com)
+          - ASEM CAN raw - 2 isolated CAN channels (www.asem.it)
 
 config CAN_TSCAN1
        tristate "TS-CAN1 PC104 boards"
index f8ff25c..9bcdefe 100644 (file)
@@ -46,7 +46,8 @@ MODULE_SUPPORTED_DEVICE("Adlink PCI-7841/cPCI-7841, "
                        "esd CAN-PCIe/2000, "
                        "Connect Tech Inc. CANpro/104-Plus Opto (CRG001), "
                        "IXXAT PC-I 04/PCI, "
-                       "ELCUS CAN-200-PCI")
+                       "ELCUS CAN-200-PCI, "
+                       "ASEM DUAL CAN-RAW")
 MODULE_LICENSE("GPL v2");
 
 #define PLX_PCI_MAX_CHAN 2
@@ -70,7 +71,9 @@ struct plx_pci_card {
                                         */
 
 #define PLX_LINT1_EN   0x1             /* Local interrupt 1 enable */
+#define PLX_LINT1_POL  (1 << 1)        /* Local interrupt 1 polarity */
 #define PLX_LINT2_EN   (1 << 3)        /* Local interrupt 2 enable */
+#define PLX_LINT2_POL  (1 << 4)        /* Local interrupt 2 polarity */
 #define PLX_PCI_INT_EN (1 << 6)        /* PCI Interrupt Enable */
 #define PLX_PCI_RESET  (1 << 30)       /* PCI Adapter Software Reset */
 
@@ -92,6 +95,9 @@ struct plx_pci_card {
  */
 #define PLX_PCI_OCR    (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
 
+/* OCR setting for ASEM Dual CAN raw */
+#define ASEM_PCI_OCR   0xfe
+
 /*
  * In the CDR register, you should set CBP to 1.
  * You will probably also want to set the clock divider value to 7
@@ -145,10 +151,20 @@ struct plx_pci_card {
 #define MOXA_PCI_VENDOR_ID             0x1393
 #define MOXA_PCI_DEVICE_ID             0x0100
 
+#define ASEM_RAW_CAN_VENDOR_ID         0x10b5
+#define ASEM_RAW_CAN_DEVICE_ID         0x9030
+#define ASEM_RAW_CAN_SUB_VENDOR_ID     0x3000
+#define ASEM_RAW_CAN_SUB_DEVICE_ID     0x1001
+#define ASEM_RAW_CAN_SUB_DEVICE_ID_BIS 0x1002
+#define ASEM_RAW_CAN_RST_REGISTER      0x54
+#define ASEM_RAW_CAN_RST_MASK_CAN1     0x20
+#define ASEM_RAW_CAN_RST_MASK_CAN2     0x04
+
 static void plx_pci_reset_common(struct pci_dev *pdev);
 static void plx9056_pci_reset_common(struct pci_dev *pdev);
 static void plx_pci_reset_marathon_pci(struct pci_dev *pdev);
 static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev);
+static void plx_pci_reset_asem_dual_can_raw(struct pci_dev *pdev);
 
 struct plx_pci_channel_map {
        u32 bar;
@@ -269,6 +285,14 @@ static struct plx_pci_card_info plx_pci_card_info_moxa = {
         /* based on PLX9052 */
 };
 
+static struct plx_pci_card_info plx_pci_card_info_asem_dual_can = {
+       "ASEM Dual CAN raw PCI", 2,
+       PLX_PCI_CAN_CLOCK, ASEM_PCI_OCR, PLX_PCI_CDR,
+       {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {4, 0x00, 0x00} },
+       &plx_pci_reset_asem_dual_can_raw
+       /* based on PLX9030 */
+};
+
 static const struct pci_device_id plx_pci_tbl[] = {
        {
                /* Adlink PCI-7841/cPCI-7841 */
@@ -375,6 +399,20 @@ static const struct pci_device_id plx_pci_tbl[] = {
                0, 0,
                (kernel_ulong_t)&plx_pci_card_info_moxa
        },
+       {
+               /* ASEM Dual CAN raw */
+               ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
+               ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID,
+               0, 0,
+               (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
+       },
+       {
+               /* ASEM Dual CAN raw -new model */
+               ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
+               ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS,
+               0, 0,
+               (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
+       },
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
@@ -524,6 +562,31 @@ static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev)
        }
 }
 
+/* Special reset function for ASEM Dual CAN raw card */
+static void plx_pci_reset_asem_dual_can_raw(struct pci_dev *pdev)
+{
+       void __iomem *bar0_addr;
+       u8 tmpval;
+
+       plx_pci_reset_common(pdev);
+
+       bar0_addr = pci_iomap(pdev, 0, 0);
+       if (!bar0_addr) {
+               dev_err(&pdev->dev, "Failed to remap reset space 0 (BAR0)\n");
+               return;
+       }
+
+       /* reset the two SJA1000 chips */
+       tmpval = ioread8(bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       tmpval &= ~(ASEM_RAW_CAN_RST_MASK_CAN1 | ASEM_RAW_CAN_RST_MASK_CAN2);
+       iowrite8(tmpval, bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       usleep_range(300, 400);
+       tmpval |= ASEM_RAW_CAN_RST_MASK_CAN1 | ASEM_RAW_CAN_RST_MASK_CAN2;
+       iowrite8(tmpval, bar0_addr + ASEM_RAW_CAN_RST_REGISTER);
+       usleep_range(300, 400);
+       pci_iounmap(pdev, bar0_addr);
+}
+
 static void plx_pci_del_card(struct pci_dev *pdev)
 {
        struct plx_pci_card *card = pci_get_drvdata(pdev);
index f3d5bda..04aac3b 100644 (file)
@@ -715,7 +715,7 @@ static void ucan_read_bulk_callback(struct urb *urb)
                                  up->in_ep_size,
                                  urb->transfer_buffer,
                                  urb->transfer_dma);
-               netdev_dbg(up->netdev, "not resumbmitting urb; status: %d\n",
+               netdev_dbg(up->netdev, "not resubmitting urb; status: %d\n",
                           urb->status);
                return;
        default:
index ed68288..80af658 100644 (file)
@@ -207,7 +207,7 @@ static int vxcan_newlink(struct net *net, struct net_device *dev,
                return PTR_ERR(peer_net);
 
        peer = rtnl_create_link(peer_net, ifname, name_assign_type,
-                               &vxcan_link_ops, tbp);
+                               &vxcan_link_ops, tbp, extack);
        if (IS_ERR(peer)) {
                put_net(peer_net);
                return PTR_ERR(peer);
index 045f084..97d0933 100644 (file)
@@ -63,6 +63,7 @@ enum xcan_reg {
        XCAN_FSR_OFFSET         = 0x00E8, /* RX FIFO Status */
        XCAN_TXMSG_BASE_OFFSET  = 0x0100, /* TX Message Space */
        XCAN_RXMSG_BASE_OFFSET  = 0x1100, /* RX Message Space */
+       XCAN_RXMSG_2_BASE_OFFSET        = 0x2100, /* RX Message Space */
 };
 
 #define XCAN_FRAME_ID_OFFSET(frame_base)       ((frame_base) + 0x00)
@@ -75,6 +76,8 @@ enum xcan_reg {
                                         XCAN_CANFD_FRAME_SIZE * (n))
 #define XCAN_RXMSG_FRAME_OFFSET(n)     (XCAN_RXMSG_BASE_OFFSET + \
                                         XCAN_CANFD_FRAME_SIZE * (n))
+#define XCAN_RXMSG_2_FRAME_OFFSET(n)   (XCAN_RXMSG_2_BASE_OFFSET + \
+                                        XCAN_CANFD_FRAME_SIZE * (n))
 
 /* the single TX mailbox used by this driver on CAN FD HW */
 #define XCAN_TX_MAILBOX_IDX            0
@@ -152,6 +155,7 @@ enum xcan_reg {
  * instead of the regular FIFO at 0x50
  */
 #define XCAN_FLAG_RX_FIFO_MULTI        0x0010
+#define XCAN_FLAG_CANFD_2      0x0020
 
 struct xcan_devtype_data {
        unsigned int flags;
@@ -221,6 +225,18 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd = {
        .brp_inc = 1,
 };
 
+static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
+       .name = DRIVER_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 256,
+       .tseg2_min = 1,
+       .tseg2_max = 128,
+       .sjw_max = 128,
+       .brp_min = 1,
+       .brp_max = 256,
+       .brp_inc = 1,
+};
+
 /**
  * xcan_write_reg_le - Write a value to the device register little endian
  * @priv:      Driver private data structure
@@ -612,7 +628,7 @@ static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev)
  *
  * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY when the tx queue is full
  */
-static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct xcan_priv *priv = netdev_priv(ndev);
        int ret;
@@ -973,7 +989,10 @@ static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv)
                if (!(fsr & XCAN_FSR_FL_MASK))
                        return -ENOENT;
 
-               offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
+               if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
+                       offset = XCAN_RXMSG_2_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
+               else
+                       offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
 
        } else {
                /* check if RX FIFO is empty */
@@ -1430,11 +1449,24 @@ static const struct xcan_devtype_data xcan_canfd_data = {
        .bus_clk_name = "s_axi_aclk",
 };
 
+static const struct xcan_devtype_data xcan_canfd2_data = {
+       .flags = XCAN_FLAG_EXT_FILTERS |
+                XCAN_FLAG_RXMNF |
+                XCAN_FLAG_TX_MAILBOXES |
+                XCAN_FLAG_CANFD_2 |
+                XCAN_FLAG_RX_FIFO_MULTI,
+       .bittiming_const = &xcan_bittiming_const_canfd2,
+       .btr_ts2_shift = XCAN_BTR_TS2_SHIFT_CANFD,
+       .btr_sjw_shift = XCAN_BTR_SJW_SHIFT_CANFD,
+       .bus_clk_name = "s_axi_aclk",
+};
+
 /* Match table for OF platform binding */
 static const struct of_device_id xcan_of_match[] = {
        { .compatible = "xlnx,zynq-can-1.0", .data = &xcan_zynq_data },
        { .compatible = "xlnx,axi-can-1.00.a", .data = &xcan_axi_data },
        { .compatible = "xlnx,canfd-1.0", .data = &xcan_canfd_data },
+       { .compatible = "xlnx,canfd-2.0", .data = &xcan_canfd2_data },
        { /* end of list */ },
 };
 MODULE_DEVICE_TABLE(of, xcan_of_match);
index 2eb6876..aa4a1f5 100644 (file)
@@ -710,6 +710,10 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds)
                return ret;
        }
 
+       ret = bcm_sf2_cfp_resume(ds);
+       if (ret)
+               return ret;
+
        if (priv->hw_params.num_gphy == 1)
                bcm_sf2_gphy_enable_set(ds, true);
 
@@ -1061,6 +1065,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
        spin_lock_init(&priv->indir_lock);
        mutex_init(&priv->stats_mutex);
        mutex_init(&priv->cfp.lock);
+       INIT_LIST_HEAD(&priv->cfp.rules_list);
 
        /* CFP rule #0 cannot be used for specific classifications, flag it as
         * permanently used
@@ -1090,12 +1095,16 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
                return ret;
        }
 
+       bcm_sf2_gphy_enable_set(priv->dev->ds, true);
+
        ret = bcm_sf2_mdio_register(ds);
        if (ret) {
                pr_err("failed to register MDIO bus\n");
                return ret;
        }
 
+       bcm_sf2_gphy_enable_set(priv->dev->ds, false);
+
        ret = bcm_sf2_cfp_rst(priv);
        if (ret) {
                pr_err("failed to reset CFP\n");
@@ -1166,6 +1175,7 @@ static int bcm_sf2_sw_remove(struct platform_device *pdev)
 
        priv->wol_ports_mask = 0;
        dsa_unregister_switch(priv->dev->ds);
+       bcm_sf2_cfp_exit(priv->dev->ds);
        /* Disable all ports and interrupts */
        bcm_sf2_sw_suspend(priv->dev->ds);
        bcm_sf2_mdio_unregister(priv);
index cc31e98..faaef32 100644 (file)
@@ -56,6 +56,7 @@ struct bcm_sf2_cfp_priv {
        DECLARE_BITMAP(used, CFP_NUM_RULES);
        DECLARE_BITMAP(unique, CFP_NUM_RULES);
        unsigned int rules_cnt;
+       struct list_head rules_list;
 };
 
 struct bcm_sf2_priv {
@@ -213,5 +214,7 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
 int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
                      struct ethtool_rxnfc *nfc);
 int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv);
+void bcm_sf2_cfp_exit(struct dsa_switch *ds);
+int bcm_sf2_cfp_resume(struct dsa_switch *ds);
 
 #endif /* __BCM_SF2_H */
index 47c5f27..e14663a 100644 (file)
 #include "bcm_sf2.h"
 #include "bcm_sf2_regs.h"
 
+struct cfp_rule {
+       int port;
+       struct ethtool_rx_flow_spec fs;
+       struct list_head next;
+};
+
 struct cfp_udf_slice_layout {
        u8 slices[UDFS_PER_SLICE];
        u32 mask_value;
@@ -515,6 +521,61 @@ static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,
        core_writel(priv, reg, offset);
 }
 
+static struct cfp_rule *bcm_sf2_cfp_rule_find(struct bcm_sf2_priv *priv,
+                                             int port, u32 location)
+{
+       struct cfp_rule *rule = NULL;
+
+       list_for_each_entry(rule, &priv->cfp.rules_list, next) {
+               if (rule->port == port && rule->fs.location == location)
+                       break;
+       }
+
+       return rule;
+}
+
+static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port,
+                               struct ethtool_rx_flow_spec *fs)
+{
+       struct cfp_rule *rule = NULL;
+       size_t fs_size = 0;
+       int ret = 1;
+
+       if (list_empty(&priv->cfp.rules_list))
+               return ret;
+
+       list_for_each_entry(rule, &priv->cfp.rules_list, next) {
+               ret = 1;
+               if (rule->port != port)
+                       continue;
+
+               if (rule->fs.flow_type != fs->flow_type ||
+                   rule->fs.ring_cookie != fs->ring_cookie ||
+                   rule->fs.m_ext.data[0] != fs->m_ext.data[0])
+                       continue;
+
+               switch (fs->flow_type & ~FLOW_EXT) {
+               case TCP_V6_FLOW:
+               case UDP_V6_FLOW:
+                       fs_size = sizeof(struct ethtool_tcpip6_spec);
+                       break;
+               case TCP_V4_FLOW:
+               case UDP_V4_FLOW:
+                       fs_size = sizeof(struct ethtool_tcpip4_spec);
+                       break;
+               default:
+                       continue;
+               }
+
+               ret = memcmp(&rule->fs.h_u, &fs->h_u, fs_size);
+               ret |= memcmp(&rule->fs.m_u, &fs->m_u, fs_size);
+               if (ret == 0)
+                       break;
+       }
+
+       return ret;
+}
+
 static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
                                     unsigned int port_num,
                                     unsigned int queue_num,
@@ -728,27 +789,14 @@ out_err:
        return ret;
 }
 
-static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
-                               struct ethtool_rx_flow_spec *fs)
+static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port,
+                                  struct ethtool_rx_flow_spec *fs)
 {
        struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
        s8 cpu_port = ds->ports[port].cpu_dp->index;
        __u64 ring_cookie = fs->ring_cookie;
        unsigned int queue_num, port_num;
-       int ret = -EINVAL;
-
-       /* Check for unsupported extensions */
-       if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype ||
-            fs->m_ext.data[1]))
-               return -EINVAL;
-
-       if (fs->location != RX_CLS_LOC_ANY &&
-           test_bit(fs->location, priv->cfp.used))
-               return -EBUSY;
-
-       if (fs->location != RX_CLS_LOC_ANY &&
-           fs->location > bcm_sf2_cfp_rule_size(priv))
-               return -EINVAL;
+       int ret;
 
        /* This rule is a Wake-on-LAN filter and we must specifically
         * target the CPU port in order for it to be working.
@@ -787,12 +835,54 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
                                                queue_num, fs);
                break;
        default:
+               ret = -EINVAL;
                break;
        }
 
        return ret;
 }
 
+static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
+                               struct ethtool_rx_flow_spec *fs)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+       struct cfp_rule *rule = NULL;
+       int ret = -EINVAL;
+
+       /* Check for unsupported extensions */
+       if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype ||
+            fs->m_ext.data[1]))
+               return -EINVAL;
+
+       if (fs->location != RX_CLS_LOC_ANY &&
+           test_bit(fs->location, priv->cfp.used))
+               return -EBUSY;
+
+       if (fs->location != RX_CLS_LOC_ANY &&
+           fs->location > bcm_sf2_cfp_rule_size(priv))
+               return -EINVAL;
+
+       ret = bcm_sf2_cfp_rule_cmp(priv, port, fs);
+       if (ret == 0)
+               return -EEXIST;
+
+       rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+       if (!rule)
+               return -ENOMEM;
+
+       ret = bcm_sf2_cfp_rule_insert(ds, port, fs);
+       if (ret) {
+               kfree(rule);
+               return ret;
+       }
+
+       rule->port = port;
+       memcpy(&rule->fs, fs, sizeof(*fs));
+       list_add_tail(&rule->next, &priv->cfp.rules_list);
+
+       return ret;
+}
+
 static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port,
                                    u32 loc, u32 *next_loc)
 {
@@ -830,19 +920,12 @@ static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port,
        return 0;
 }
 
-static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port,
-                               u32 loc)
+static int bcm_sf2_cfp_rule_remove(struct bcm_sf2_priv *priv, int port,
+                                  u32 loc)
 {
        u32 next_loc = 0;
        int ret;
 
-       /* Refuse deleting unused rules, and those that are not unique since
-        * that could leave IPv6 rules with one of the chained rule in the
-        * table.
-        */
-       if (!test_bit(loc, priv->cfp.unique) || loc == 0)
-               return -EINVAL;
-
        ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc);
        if (ret)
                return ret;
@@ -854,318 +937,54 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port,
        return ret;
 }
 
-static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow)
+static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port, u32 loc)
 {
-       unsigned int i;
-
-       for (i = 0; i < sizeof(flow->m_u); i++)
-               flow->m_u.hdata[i] ^= 0xff;
-
-       flow->m_ext.vlan_etype ^= cpu_to_be16(~0);
-       flow->m_ext.vlan_tci ^= cpu_to_be16(~0);
-       flow->m_ext.data[0] ^= cpu_to_be32(~0);
-       flow->m_ext.data[1] ^= cpu_to_be32(~0);
-}
-
-static int bcm_sf2_cfp_unslice_ipv4(struct bcm_sf2_priv *priv,
-                                   struct ethtool_tcpip4_spec *v4_spec,
-                                   bool mask)
-{
-       u32 reg, offset, ipv4;
-       u16 src_dst_port;
-
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(3);
-       else
-               offset = CORE_CFP_DATA_PORT(3);
-
-       reg = core_readl(priv, offset);
-       /* src port [15:8] */
-       src_dst_port = reg << 8;
-
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(2);
-       else
-               offset = CORE_CFP_DATA_PORT(2);
-
-       reg = core_readl(priv, offset);
-       /* src port [7:0] */
-       src_dst_port |= (reg >> 24);
-
-       v4_spec->pdst = cpu_to_be16(src_dst_port);
-       v4_spec->psrc = cpu_to_be16((u16)(reg >> 8));
-
-       /* IPv4 dst [15:8] */
-       ipv4 = (reg & 0xff) << 8;
-
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(1);
-       else
-               offset = CORE_CFP_DATA_PORT(1);
-
-       reg = core_readl(priv, offset);
-       /* IPv4 dst [31:16] */
-       ipv4 |= ((reg >> 8) & 0xffff) << 16;
-       /* IPv4 dst [7:0] */
-       ipv4 |= (reg >> 24) & 0xff;
-       v4_spec->ip4dst = cpu_to_be32(ipv4);
-
-       /* IPv4 src [15:8] */
-       ipv4 = (reg & 0xff) << 8;
-
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(0);
-       else
-               offset = CORE_CFP_DATA_PORT(0);
-       reg = core_readl(priv, offset);
+       struct cfp_rule *rule;
+       int ret;
 
-       /* Once the TCAM is programmed, the mask reflects the slice number
-        * being matched, don't bother checking it when reading back the
-        * mask spec
+       /* Refuse deleting unused rules, and those that are not unique since
+        * that could leave IPv6 rules with one of the chained rule in the
+        * table.
         */
-       if (!mask && !(reg & SLICE_VALID))
+       if (!test_bit(loc, priv->cfp.unique) || loc == 0)
                return -EINVAL;
 
-       /* IPv4 src [7:0] */
-       ipv4 |= (reg >> 24) & 0xff;
-       /* IPv4 src [31:16] */
-       ipv4 |= ((reg >> 8) & 0xffff) << 16;
-       v4_spec->ip4src = cpu_to_be32(ipv4);
-
-       return 0;
-}
-
-static int bcm_sf2_cfp_ipv4_rule_get(struct bcm_sf2_priv *priv, int port,
-                                    struct ethtool_rx_flow_spec *fs)
-{
-       struct ethtool_tcpip4_spec *v4_spec = NULL, *v4_m_spec = NULL;
-       u32 reg;
-       int ret;
-
-       reg = core_readl(priv, CORE_CFP_DATA_PORT(6));
-
-       switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) {
-       case IPPROTO_TCP:
-               fs->flow_type = TCP_V4_FLOW;
-               v4_spec = &fs->h_u.tcp_ip4_spec;
-               v4_m_spec = &fs->m_u.tcp_ip4_spec;
-               break;
-       case IPPROTO_UDP:
-               fs->flow_type = UDP_V4_FLOW;
-               v4_spec = &fs->h_u.udp_ip4_spec;
-               v4_m_spec = &fs->m_u.udp_ip4_spec;
-               break;
-       default:
+       rule = bcm_sf2_cfp_rule_find(priv, port, loc);
+       if (!rule)
                return -EINVAL;
-       }
-
-       fs->m_ext.data[0] = cpu_to_be32((reg >> IP_FRAG_SHIFT) & 1);
-       v4_spec->tos = (reg >> IPTOS_SHIFT) & IPTOS_MASK;
-
-       ret = bcm_sf2_cfp_unslice_ipv4(priv, v4_spec, false);
-       if (ret)
-               return ret;
-
-       return bcm_sf2_cfp_unslice_ipv4(priv, v4_m_spec, true);
-}
-
-static int bcm_sf2_cfp_unslice_ipv6(struct bcm_sf2_priv *priv,
-                                    __be32 *ip6_addr, __be16 *port,
-                                    bool mask)
-{
-       u32 reg, tmp, offset;
-
-       /* C-Tag                [31:24]
-        * UDF_n_B8             [23:8] (port)
-        * UDF_n_B7 (upper)     [7:0] (addr[15:8])
-        */
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(4);
-       else
-               offset = CORE_CFP_DATA_PORT(4);
-       reg = core_readl(priv, offset);
-       *port = cpu_to_be32(reg) >> 8;
-       tmp = (u32)(reg & 0xff) << 8;
-
-       /* UDF_n_B7 (lower)     [31:24] (addr[7:0])
-        * UDF_n_B6             [23:8] (addr[31:16])
-        * UDF_n_B5 (upper)     [7:0] (addr[47:40])
-        */
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(3);
-       else
-               offset = CORE_CFP_DATA_PORT(3);
-       reg = core_readl(priv, offset);
-       tmp |= (reg >> 24) & 0xff;
-       tmp |= (u32)((reg >> 8) << 16);
-       ip6_addr[3] = cpu_to_be32(tmp);
-       tmp = (u32)(reg & 0xff) << 8;
-
-       /* UDF_n_B5 (lower)     [31:24] (addr[39:32])
-        * UDF_n_B4             [23:8] (addr[63:48])
-        * UDF_n_B3 (upper)     [7:0] (addr[79:72])
-        */
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(2);
-       else
-               offset = CORE_CFP_DATA_PORT(2);
-       reg = core_readl(priv, offset);
-       tmp |= (reg >> 24) & 0xff;
-       tmp |= (u32)((reg >> 8) << 16);
-       ip6_addr[2] = cpu_to_be32(tmp);
-       tmp = (u32)(reg & 0xff) << 8;
 
-       /* UDF_n_B3 (lower)     [31:24] (addr[71:64])
-        * UDF_n_B2             [23:8] (addr[95:80])
-        * UDF_n_B1 (upper)     [7:0] (addr[111:104])
-        */
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(1);
-       else
-               offset = CORE_CFP_DATA_PORT(1);
-       reg = core_readl(priv, offset);
-       tmp |= (reg >> 24) & 0xff;
-       tmp |= (u32)((reg >> 8) << 16);
-       ip6_addr[1] = cpu_to_be32(tmp);
-       tmp = (u32)(reg & 0xff) << 8;
+       ret = bcm_sf2_cfp_rule_remove(priv, port, loc);
 
-       /* UDF_n_B1 (lower)     [31:24] (addr[103:96])
-        * UDF_n_B0             [23:8] (addr[127:112])
-        * Reserved             [7:4]
-        * Slice ID             [3:2]
-        * Slice valid          [1:0]
-        */
-       if (mask)
-               offset = CORE_CFP_MASK_PORT(0);
-       else
-               offset = CORE_CFP_DATA_PORT(0);
-       reg = core_readl(priv, offset);
-       tmp |= (reg >> 24) & 0xff;
-       tmp |= (u32)((reg >> 8) << 16);
-       ip6_addr[0] = cpu_to_be32(tmp);
+       list_del(&rule->next);
+       kfree(rule);
 
-       if (!mask && !(reg & SLICE_VALID))
-               return -EINVAL;
-
-       return 0;
+       return ret;
 }
 
-static int bcm_sf2_cfp_ipv6_rule_get(struct bcm_sf2_priv *priv, int port,
-                                    struct ethtool_rx_flow_spec *fs,
-                                    u32 next_loc)
+static void bcm_sf2_invert_masks(struct ethtool_rx_flow_spec *flow)
 {
-       struct ethtool_tcpip6_spec *v6_spec = NULL, *v6_m_spec = NULL;
-       u32 reg;
-       int ret;
-
-       /* UDPv6 and TCPv6 both use ethtool_tcpip6_spec so we are fine
-        * assuming tcp_ip6_spec here being an union.
-        */
-       v6_spec = &fs->h_u.tcp_ip6_spec;
-       v6_m_spec = &fs->m_u.tcp_ip6_spec;
-
-       /* Read the second half first */
-       ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6dst, &v6_spec->pdst,
-                                      false);
-       if (ret)
-               return ret;
-
-       ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6dst,
-                                      &v6_m_spec->pdst, true);
-       if (ret)
-               return ret;
-
-       /* Read last to avoid next entry clobbering the results during search
-        * operations. We would not have the port enabled for this rule, so
-        * don't bother checking it.
-        */
-       (void)core_readl(priv, CORE_CFP_DATA_PORT(7));
-
-       /* The slice number is valid, so read the rule we are chained from now
-        * which is our first half.
-        */
-       bcm_sf2_cfp_rule_addr_set(priv, next_loc);
-       ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);
-       if (ret)
-               return ret;
-
-       reg = core_readl(priv, CORE_CFP_DATA_PORT(6));
-
-       switch ((reg & IPPROTO_MASK) >> IPPROTO_SHIFT) {
-       case IPPROTO_TCP:
-               fs->flow_type = TCP_V6_FLOW;
-               break;
-       case IPPROTO_UDP:
-               fs->flow_type = UDP_V6_FLOW;
-               break;
-       default:
-               return -EINVAL;
-       }
+       unsigned int i;
 
-       ret = bcm_sf2_cfp_unslice_ipv6(priv, v6_spec->ip6src, &v6_spec->psrc,
-                                      false);
-       if (ret)
-               return ret;
+       for (i = 0; i < sizeof(flow->m_u); i++)
+               flow->m_u.hdata[i] ^= 0xff;
 
-       return bcm_sf2_cfp_unslice_ipv6(priv, v6_m_spec->ip6src,
-                                       &v6_m_spec->psrc, true);
+       flow->m_ext.vlan_etype ^= cpu_to_be16(~0);
+       flow->m_ext.vlan_tci ^= cpu_to_be16(~0);
+       flow->m_ext.data[0] ^= cpu_to_be32(~0);
+       flow->m_ext.data[1] ^= cpu_to_be32(~0);
 }
 
 static int bcm_sf2_cfp_rule_get(struct bcm_sf2_priv *priv, int port,
                                struct ethtool_rxnfc *nfc)
 {
-       u32 reg, ipv4_or_chain_id;
-       unsigned int queue_num;
-       int ret;
-
-       bcm_sf2_cfp_rule_addr_set(priv, nfc->fs.location);
-
-       ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | ACT_POL_RAM);
-       if (ret)
-               return ret;
-
-       reg = core_readl(priv, CORE_ACT_POL_DATA0);
-
-       ret = bcm_sf2_cfp_op(priv, OP_SEL_READ | TCAM_SEL);
-       if (ret)
-               return ret;
-
-       /* Extract the destination port */
-       nfc->fs.ring_cookie = fls((reg >> DST_MAP_IB_SHIFT) &
-                                 DST_MAP_IB_MASK) - 1;
-
-       /* There is no Port 6, so we compensate for that here */
-       if (nfc->fs.ring_cookie >= 6)
-               nfc->fs.ring_cookie++;
-       nfc->fs.ring_cookie *= SF2_NUM_EGRESS_QUEUES;
-
-       /* Extract the destination queue */
-       queue_num = (reg >> NEW_TC_SHIFT) & NEW_TC_MASK;
-       nfc->fs.ring_cookie += queue_num;
-
-       /* Extract the L3_FRAMING or CHAIN_ID */
-       reg = core_readl(priv, CORE_CFP_DATA_PORT(6));
+       struct cfp_rule *rule;
 
-       /* With IPv6 rules this would contain a non-zero chain ID since
-        * we reserve entry 0 and it cannot be used. So if we read 0 here
-        * this means an IPv4 rule.
-        */
-       ipv4_or_chain_id = (reg >> L3_FRAMING_SHIFT) & 0xff;
-       if (ipv4_or_chain_id == 0)
-               ret = bcm_sf2_cfp_ipv4_rule_get(priv, port, &nfc->fs);
-       else
-               ret = bcm_sf2_cfp_ipv6_rule_get(priv, port, &nfc->fs,
-                                               ipv4_or_chain_id);
-       if (ret)
-               return ret;
-
-       /* Read last to avoid next entry clobbering the results during search
-        * operations
-        */
-       reg = core_readl(priv, CORE_CFP_DATA_PORT(7));
-       if (!(reg & 1 << port))
+       rule = bcm_sf2_cfp_rule_find(priv, port, nfc->fs.location);
+       if (!rule)
                return -EINVAL;
 
+       memcpy(&nfc->fs, &rule->fs, sizeof(rule->fs));
+
        bcm_sf2_invert_masks(&nfc->fs);
 
        /* Put the TCAM size here */
@@ -1302,3 +1121,51 @@ int bcm_sf2_cfp_rst(struct bcm_sf2_priv *priv)
 
        return 0;
 }
+
+void bcm_sf2_cfp_exit(struct dsa_switch *ds)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+       struct cfp_rule *rule, *n;
+
+       if (list_empty(&priv->cfp.rules_list))
+               return;
+
+       list_for_each_entry_safe_reverse(rule, n, &priv->cfp.rules_list, next)
+               bcm_sf2_cfp_rule_del(priv, rule->port, rule->fs.location);
+}
+
+int bcm_sf2_cfp_resume(struct dsa_switch *ds)
+{
+       struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+       struct cfp_rule *rule;
+       int ret = 0;
+       u32 reg;
+
+       if (list_empty(&priv->cfp.rules_list))
+               return ret;
+
+       reg = core_readl(priv, CORE_CFP_CTL_REG);
+       reg &= ~CFP_EN_MAP_MASK;
+       core_writel(priv, reg, CORE_CFP_CTL_REG);
+
+       ret = bcm_sf2_cfp_rst(priv);
+       if (ret)
+               return ret;
+
+       list_for_each_entry(rule, &priv->cfp.rules_list, next) {
+               ret = bcm_sf2_cfp_rule_remove(priv, rule->port,
+                                             rule->fs.location);
+               if (ret) {
+                       dev_err(ds->dev, "failed to remove rule\n");
+                       return ret;
+               }
+
+               ret = bcm_sf2_cfp_rule_insert(ds, rule->port, &rule->fs);
+               if (ret) {
+                       dev_err(ds->dev, "failed to restore rule\n");
+                       return ret;
+               }
+       }
+
+       return ret;
+}
index a8b8f59..a8caf92 100644 (file)
@@ -1,12 +1,16 @@
-menuconfig MICROCHIP_KSZ
-       tristate "Microchip KSZ series switch support"
+config NET_DSA_MICROCHIP_KSZ_COMMON
+       tristate
+
+menuconfig NET_DSA_MICROCHIP_KSZ9477
+       tristate "Microchip KSZ9477 series switch support"
        depends on NET_DSA
        select NET_DSA_TAG_KSZ
+       select NET_DSA_MICROCHIP_KSZ_COMMON
        help
-         This driver adds support for Microchip KSZ switch chips.
+         This driver adds support for Microchip KSZ9477 switch chips.
 
-config MICROCHIP_KSZ_SPI_DRIVER
-       tristate "KSZ series SPI connected switch driver"
-       depends on MICROCHIP_KSZ && SPI
+config NET_DSA_MICROCHIP_KSZ9477_SPI
+       tristate "KSZ9477 series SPI connected switch driver"
+       depends on NET_DSA_MICROCHIP_KSZ9477 && SPI
        help
          Select to enable support for registering switches configured through SPI.
index ed335e2..3142c18 100644 (file)
@@ -1,2 +1,3 @@
-obj-$(CONFIG_MICROCHIP_KSZ)            += ksz_common.o
-obj-$(CONFIG_MICROCHIP_KSZ_SPI_DRIVER) += ksz_spi.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON)     += ksz_common.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477)                += ksz9477.o
+obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI)    += ksz9477_spi.o
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
new file mode 100644 (file)
index 0000000..0684657
--- /dev/null
@@ -0,0 +1,1316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ9477 switch driver main logic
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/microchip-ksz.h>
+#include <linux/phy.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <net/dsa.h>
+#include <net/switchdev.h>
+
+#include "ksz_priv.h"
+#include "ksz_common.h"
+#include "ksz9477_reg.h"
+
+static const struct {
+       int index;
+       char string[ETH_GSTRING_LEN];
+} ksz9477_mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
+       { 0x00, "rx_hi" },
+       { 0x01, "rx_undersize" },
+       { 0x02, "rx_fragments" },
+       { 0x03, "rx_oversize" },
+       { 0x04, "rx_jabbers" },
+       { 0x05, "rx_symbol_err" },
+       { 0x06, "rx_crc_err" },
+       { 0x07, "rx_align_err" },
+       { 0x08, "rx_mac_ctrl" },
+       { 0x09, "rx_pause" },
+       { 0x0A, "rx_bcast" },
+       { 0x0B, "rx_mcast" },
+       { 0x0C, "rx_ucast" },
+       { 0x0D, "rx_64_or_less" },
+       { 0x0E, "rx_65_127" },
+       { 0x0F, "rx_128_255" },
+       { 0x10, "rx_256_511" },
+       { 0x11, "rx_512_1023" },
+       { 0x12, "rx_1024_1522" },
+       { 0x13, "rx_1523_2000" },
+       { 0x14, "rx_2001" },
+       { 0x15, "tx_hi" },
+       { 0x16, "tx_late_col" },
+       { 0x17, "tx_pause" },
+       { 0x18, "tx_bcast" },
+       { 0x19, "tx_mcast" },
+       { 0x1A, "tx_ucast" },
+       { 0x1B, "tx_deferred" },
+       { 0x1C, "tx_total_col" },
+       { 0x1D, "tx_exc_col" },
+       { 0x1E, "tx_single_col" },
+       { 0x1F, "tx_mult_col" },
+       { 0x80, "rx_total" },
+       { 0x81, "tx_total" },
+       { 0x82, "rx_discards" },
+       { 0x83, "tx_discards" },
+};
+
+static void ksz9477_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
+{
+       u32 data;
+
+       ksz_read32(dev, addr, &data);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       ksz_write32(dev, addr, data);
+}
+
+static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset,
+                              u32 bits, bool set)
+{
+       u32 addr;
+       u32 data;
+
+       addr = PORT_CTRL_ADDR(port, offset);
+       ksz_read32(dev, addr, &data);
+
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+
+       ksz_write32(dev, addr, data);
+}
+
+static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton,
+                                       int timeout)
+{
+       u8 data;
+
+       do {
+               ksz_read8(dev, REG_SW_VLAN_CTRL, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_get_vlan_table(struct ksz_device *dev, u16 vid,
+                                 u32 *vlan_table)
+{
+       int ret;
+
+       mutex_lock(&dev->vlan_mutex);
+
+       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
+
+       /* wait to be cleared */
+       ret = ksz9477_wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read vlan table\n");
+               goto exit;
+       }
+
+       ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
+       ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
+       ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
+
+       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+exit:
+       mutex_unlock(&dev->vlan_mutex);
+
+       return ret;
+}
+
+static int ksz9477_set_vlan_table(struct ksz_device *dev, u16 vid,
+                                 u32 *vlan_table)
+{
+       int ret;
+
+       mutex_lock(&dev->vlan_mutex);
+
+       ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
+       ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
+       ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
+
+       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
+       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
+
+       /* wait to be cleared */
+       ret = ksz9477_wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to write vlan table\n");
+               goto exit;
+       }
+
+       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
+
+       /* update vlan cache table */
+       dev->vlan_cache[vid].table[0] = vlan_table[0];
+       dev->vlan_cache[vid].table[1] = vlan_table[1];
+       dev->vlan_cache[vid].table[2] = vlan_table[2];
+
+exit:
+       mutex_unlock(&dev->vlan_mutex);
+
+       return ret;
+}
+
+static void ksz9477_read_table(struct ksz_device *dev, u32 *table)
+{
+       ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
+       ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
+       ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
+       ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
+}
+
+static void ksz9477_write_table(struct ksz_device *dev, u32 *table)
+{
+       ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
+       ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
+       ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
+       ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
+}
+
+static int ksz9477_wait_alu_ready(struct ksz_device *dev, u32 waiton,
+                                 int timeout)
+{
+       u32 data;
+
+       do {
+               ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_wait_alu_sta_ready(struct ksz_device *dev, u32 waiton,
+                                     int timeout)
+{
+       u32 data;
+
+       do {
+               ksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);
+               if (!(data & waiton))
+                       break;
+               usleep_range(1, 10);
+       } while (timeout-- > 0);
+
+       if (timeout <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ksz9477_reset_switch(struct ksz_device *dev)
+{
+       u8 data8;
+       u16 data16;
+       u32 data32;
+
+       /* reset switch */
+       ksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
+
+       /* turn off SPI DO Edge select */
+       ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+       data8 &= ~SPI_AUTO_EDGE_DETECTION;
+       ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+
+       /* default configuration */
+       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
+       data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
+             SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;
+       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+
+       /* disable interrupts */
+       ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
+       ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);
+       ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
+
+       /* set broadcast storm protection 10% rate */
+       ksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);
+       data16 &= ~BROADCAST_STORM_RATE;
+       data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
+       ksz_write16(dev, REG_SW_MAC_CTRL_2, data16);
+
+       return 0;
+}
+
+static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds,
+                                                     int port)
+{
+       return DSA_TAG_PROTO_KSZ;
+}
+
+static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
+{
+       struct ksz_device *dev = ds->priv;
+       u16 val = 0xffff;
+
+       /* No real PHY after this. Simulate the PHY.
+        * A fixed PHY can be setup in the device tree, but this function is
+        * still called for that port during initialization.
+        * For RGMII PHY there is no way to access it so the fixed PHY should
+        * be used.  For SGMII PHY the supporting code will be added later.
+        */
+       if (addr >= dev->phy_port_cnt) {
+               struct ksz_port *p = &dev->ports[addr];
+
+               switch (reg) {
+               case MII_BMCR:
+                       val = 0x1140;
+                       break;
+               case MII_BMSR:
+                       val = 0x796d;
+                       break;
+               case MII_PHYSID1:
+                       val = 0x0022;
+                       break;
+               case MII_PHYSID2:
+                       val = 0x1631;
+                       break;
+               case MII_ADVERTISE:
+                       val = 0x05e1;
+                       break;
+               case MII_LPA:
+                       val = 0xc5e1;
+                       break;
+               case MII_CTRL1000:
+                       val = 0x0700;
+                       break;
+               case MII_STAT1000:
+                       if (p->phydev.speed == SPEED_1000)
+                               val = 0x3800;
+                       else
+                               val = 0;
+                       break;
+               }
+       } else {
+               ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
+       }
+
+       return val;
+}
+
+static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg,
+                              u16 val)
+{
+       struct ksz_device *dev = ds->priv;
+
+       /* No real PHY after this. */
+       if (addr >= dev->phy_port_cnt)
+               return 0;
+       ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
+
+       return 0;
+}
+
+static void ksz9477_get_strings(struct dsa_switch *ds, int port,
+                               u32 stringset, uint8_t *buf)
+{
+       int i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+               memcpy(buf + i * ETH_GSTRING_LEN, ksz9477_mib_names[i].string,
+                      ETH_GSTRING_LEN);
+       }
+}
+
+static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                 uint64_t *buf)
+{
+       struct ksz_device *dev = ds->priv;
+       int i;
+       u32 data;
+       int timeout;
+
+       mutex_lock(&dev->stats_mutex);
+
+       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
+               data = MIB_COUNTER_READ;
+               data |= ((ksz9477_mib_names[i].index & 0xFF) <<
+                       MIB_COUNTER_INDEX_S);
+               ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
+
+               timeout = 1000;
+               do {
+                       ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
+                                   &data);
+                       usleep_range(1, 10);
+                       if (!(data & MIB_COUNTER_READ))
+                               break;
+               } while (timeout-- > 0);
+
+               /* failed to read MIB. get out of loop */
+               if (!timeout) {
+                       dev_dbg(dev->dev, "Failed to get MIB\n");
+                       break;
+               }
+
+               /* count resets upon read */
+               ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+
+               dev->mib_value[i] += (uint64_t)data;
+               buf[i] = dev->mib_value[i];
+       }
+
+       mutex_unlock(&dev->stats_mutex);
+}
+
+static void ksz9477_cfg_port_member(struct ksz_device *dev, int port,
+                                   u8 member)
+{
+       ksz_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member);
+       dev->ports[port].member = member;
+}
+
+static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port,
+                                      u8 state)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p = &dev->ports[port];
+       u8 data;
+       int member = -1;
+
+       ksz_pread8(dev, port, P_STP_CTRL, &data);
+       data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               data |= PORT_LEARN_DISABLE;
+               if (port != dev->cpu_port)
+                       member = 0;
+               break;
+       case BR_STATE_LISTENING:
+               data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
+               if (port != dev->cpu_port &&
+                   p->stp_state == BR_STATE_DISABLED)
+                       member = dev->host_mask | p->vid_member;
+               break;
+       case BR_STATE_LEARNING:
+               data |= PORT_RX_ENABLE;
+               break;
+       case BR_STATE_FORWARDING:
+               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+
+               /* This function is also used internally. */
+               if (port == dev->cpu_port)
+                       break;
+
+               member = dev->host_mask | p->vid_member;
+
+               /* Port is a member of a bridge. */
+               if (dev->br_member & (1 << port)) {
+                       dev->member |= (1 << port);
+                       member = dev->member;
+               }
+               break;
+       case BR_STATE_BLOCKING:
+               data |= PORT_LEARN_DISABLE;
+               if (port != dev->cpu_port &&
+                   p->stp_state == BR_STATE_DISABLED)
+                       member = dev->host_mask | p->vid_member;
+               break;
+       default:
+               dev_err(ds->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       p->stp_state = state;
+       if (data & PORT_RX_ENABLE)
+               dev->rx_ports |= (1 << port);
+       else
+               dev->rx_ports &= ~(1 << port);
+       if (data & PORT_TX_ENABLE)
+               dev->tx_ports |= (1 << port);
+       else
+               dev->tx_ports &= ~(1 << port);
+
+       /* Port membership may share register with STP state. */
+       if (member >= 0 && member != p->member)
+               ksz9477_cfg_port_member(dev, port, (u8)member);
+
+       /* Check if forwarding needs to be updated. */
+       if (state != BR_STATE_FORWARDING) {
+               if (dev->br_member & (1 << port))
+                       dev->member &= ~(1 << port);
+       }
+
+       /* When topology has changed the function ksz_update_port_member
+        * should be called to modify port forwarding behavior.  However
+        * as the offload_fwd_mark indication cannot be reported here
+        * the switch forwarding function is not enabled.
+        */
+}
+
+static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)
+{
+       u8 data;
+
+       ksz_read8(dev, REG_SW_LUE_CTRL_2, &data);
+       data &= ~(SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S);
+       data |= (SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S);
+       ksz_write8(dev, REG_SW_LUE_CTRL_2, data);
+       if (port < dev->mib_port_cnt) {
+               /* flush individual port */
+               ksz_pread8(dev, port, P_STP_CTRL, &data);
+               if (!(data & PORT_LEARN_DISABLE))
+                       ksz_pwrite8(dev, port, P_STP_CTRL,
+                                   data | PORT_LEARN_DISABLE);
+               ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
+               ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       } else {
+               /* flush all */
+               ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_STP_TABLE, true);
+       }
+}
+
+static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
+                                      bool flag)
+{
+       struct ksz_device *dev = ds->priv;
+
+       if (flag) {
+               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+                            PORT_VLAN_LOOKUP_VID_0, true);
+               ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
+                             true);
+               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
+       } else {
+               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
+               ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
+                             false);
+               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
+                            PORT_VLAN_LOOKUP_VID_0, false);
+       }
+
+       return 0;
+}
+
+static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 vlan_table[3];
+       u16 vid;
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to get vlan table\n");
+                       return;
+               }
+
+               vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
+               if (untagged)
+                       vlan_table[1] |= BIT(port);
+               else
+                       vlan_table[1] &= ~BIT(port);
+               vlan_table[1] &= ~(BIT(dev->cpu_port));
+
+               vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
+
+               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to set vlan table\n");
+                       return;
+               }
+
+               /* change PVID */
+               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+                       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
+       }
+}
+
+static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan)
+{
+       struct ksz_device *dev = ds->priv;
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       u32 vlan_table[3];
+       u16 vid;
+       u16 pvid;
+
+       ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
+       pvid = pvid & 0xFFF;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to get vlan table\n");
+                       return -ETIMEDOUT;
+               }
+
+               vlan_table[2] &= ~BIT(port);
+
+               if (pvid == vid)
+                       pvid = 1;
+
+               if (untagged)
+                       vlan_table[1] &= ~BIT(port);
+
+               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
+                       dev_dbg(dev->dev, "Failed to set vlan table\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
+
+       return 0;
+}
+
+static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 alu_table[4];
+       u32 data;
+       int ret = 0;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* find any entry with mac & vid */
+       data = vid << ALU_FID_INDEX_S;
+       data |= ((addr[0] << 8) | addr[1]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+       data = ((addr[2] << 24) | (addr[3] << 16));
+       data |= ((addr[4] << 8) | addr[5]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+       /* start read operation */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read ALU\n");
+               goto exit;
+       }
+
+       /* read ALU entry */
+       ksz9477_read_table(dev, alu_table);
+
+       /* update ALU entry */
+       alu_table[0] = ALU_V_STATIC_VALID;
+       alu_table[1] |= BIT(port);
+       if (vid)
+               alu_table[1] |= ALU_V_USE_FID;
+       alu_table[2] = (vid << ALU_V_FID_S);
+       alu_table[2] |= ((addr[0] << 8) | addr[1]);
+       alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
+       alu_table[3] |= ((addr[4] << 8) | addr[5]);
+
+       ksz9477_write_table(dev, alu_table);
+
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to write ALU\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static int ksz9477_port_fdb_del(struct dsa_switch *ds, int port,
+                               const unsigned char *addr, u16 vid)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 alu_table[4];
+       u32 data;
+       int ret = 0;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* read any entry with mac & vid */
+       data = vid << ALU_FID_INDEX_S;
+       data |= ((addr[0] << 8) | addr[1]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
+
+       data = ((addr[2] << 24) | (addr[3] << 16));
+       data |= ((addr[4] << 8) | addr[5]);
+       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
+
+       /* start read operation */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0) {
+               dev_dbg(dev->dev, "Failed to read ALU\n");
+               goto exit;
+       }
+
+       ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
+       if (alu_table[0] & ALU_V_STATIC_VALID) {
+               ksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);
+               ksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);
+               ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
+
+               /* clear forwarding port */
+               alu_table[2] &= ~BIT(port);
+
+               /* if there is no port to forward, clear table */
+               if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
+                       alu_table[0] = 0;
+                       alu_table[1] = 0;
+                       alu_table[2] = 0;
+                       alu_table[3] = 0;
+               }
+       } else {
+               alu_table[0] = 0;
+               alu_table[1] = 0;
+               alu_table[2] = 0;
+               alu_table[3] = 0;
+       }
+
+       ksz9477_write_table(dev, alu_table);
+
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_ready(dev, ALU_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to write ALU\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static void ksz9477_convert_alu(struct alu_struct *alu, u32 *alu_table)
+{
+       alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
+       alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
+       alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
+       alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
+                       ALU_V_PRIO_AGE_CNT_M;
+       alu->mstp = alu_table[0] & ALU_V_MSTP_M;
+
+       alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
+       alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
+       alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
+
+       alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
+
+       alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
+       alu->mac[1] = alu_table[2] & 0xFF;
+       alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
+       alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
+       alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
+       alu->mac[5] = alu_table[3] & 0xFF;
+}
+
+static int ksz9477_port_fdb_dump(struct dsa_switch *ds, int port,
+                                dsa_fdb_dump_cb_t *cb, void *data)
+{
+       struct ksz_device *dev = ds->priv;
+       int ret = 0;
+       u32 ksz_data;
+       u32 alu_table[4];
+       struct alu_struct alu;
+       int timeout;
+
+       mutex_lock(&dev->alu_mutex);
+
+       /* start ALU search */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);
+
+       do {
+               timeout = 1000;
+               do {
+                       ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);
+                       if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))
+                               break;
+                       usleep_range(1, 10);
+               } while (timeout-- > 0);
+
+               if (!timeout) {
+                       dev_dbg(dev->dev, "Failed to search ALU\n");
+                       ret = -ETIMEDOUT;
+                       goto exit;
+               }
+
+               /* read ALU table */
+               ksz9477_read_table(dev, alu_table);
+
+               ksz9477_convert_alu(&alu, alu_table);
+
+               if (alu.port_forward & BIT(port)) {
+                       ret = cb(alu.mac, alu.fid, alu.is_static, data);
+                       if (ret)
+                               goto exit;
+               }
+       } while (ksz_data & ALU_START);
+
+exit:
+
+       /* stop ALU search */
+       ksz_write32(dev, REG_SW_ALU_CTRL__4, 0);
+
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_mdb *mdb)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 static_table[4];
+       u32 data;
+       int index;
+       u32 mac_hi, mac_lo;
+
+       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+       mutex_lock(&dev->alu_mutex);
+
+       for (index = 0; index < dev->num_statics; index++) {
+               /* find empty slot first */
+               data = (index << ALU_STAT_INDEX_S) |
+                       ALU_STAT_READ | ALU_STAT_START;
+               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+               /* wait to be finished */
+               if (ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {
+                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+                       goto exit;
+               }
+
+               /* read ALU static table */
+               ksz9477_read_table(dev, static_table);
+
+               if (static_table[0] & ALU_V_STATIC_VALID) {
+                       /* check this has same vid & mac address */
+                       if (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&
+                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+                           static_table[3] == mac_lo) {
+                               /* found matching one */
+                               break;
+                       }
+               } else {
+                       /* found empty one */
+                       break;
+               }
+       }
+
+       /* no available entry */
+       if (index == dev->num_statics)
+               goto exit;
+
+       /* add entry */
+       static_table[0] = ALU_V_STATIC_VALID;
+       static_table[1] |= BIT(port);
+       if (mdb->vid)
+               static_table[1] |= ALU_V_USE_FID;
+       static_table[2] = (mdb->vid << ALU_V_FID_S);
+       static_table[2] |= mac_hi;
+       static_table[3] = mac_lo;
+
+       ksz9477_write_table(dev, static_table);
+
+       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+       /* wait to be finished */
+       if (ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)
+               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+}
+
+static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb)
+{
+       struct ksz_device *dev = ds->priv;
+       u32 static_table[4];
+       u32 data;
+       int index;
+       int ret = 0;
+       u32 mac_hi, mac_lo;
+
+       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
+       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
+       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
+
+       mutex_lock(&dev->alu_mutex);
+
+       for (index = 0; index < dev->num_statics; index++) {
+               /* find empty slot first */
+               data = (index << ALU_STAT_INDEX_S) |
+                       ALU_STAT_READ | ALU_STAT_START;
+               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+               /* wait to be finished */
+               ret = ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+               if (ret < 0) {
+                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+                       goto exit;
+               }
+
+               /* read ALU static table */
+               ksz9477_read_table(dev, static_table);
+
+               if (static_table[0] & ALU_V_STATIC_VALID) {
+                       /* check this has same vid & mac address */
+
+                       if (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&
+                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
+                           static_table[3] == mac_lo) {
+                               /* found matching one */
+                               break;
+                       }
+               }
+       }
+
+       /* no available entry */
+       if (index == dev->num_statics)
+               goto exit;
+
+       /* clear port */
+       static_table[1] &= ~BIT(port);
+
+       if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
+               /* delete entry */
+               static_table[0] = 0;
+               static_table[1] = 0;
+               static_table[2] = 0;
+               static_table[3] = 0;
+       }
+
+       ksz9477_write_table(dev, static_table);
+
+       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
+       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+
+       /* wait to be finished */
+       ret = ksz9477_wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
+       if (ret < 0)
+               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+
+exit:
+       mutex_unlock(&dev->alu_mutex);
+
+       return ret;
+}
+
+static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port,
+                                  struct dsa_mall_mirror_tc_entry *mirror,
+                                  bool ingress)
+{
+       struct ksz_device *dev = ds->priv;
+
+       if (ingress)
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
+       else
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
+
+       ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
+
+       /* configure mirror port */
+       ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+                    PORT_MIRROR_SNIFFER, true);
+
+       ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+       return 0;
+}
+
+static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
+                                   struct dsa_mall_mirror_tc_entry *mirror)
+{
+       struct ksz_device *dev = ds->priv;
+       u8 data;
+
+       if (mirror->ingress)
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
+       else
+               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
+
+       ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
+
+       if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
+               ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
+                            PORT_MIRROR_SNIFFER, false);
+}
+
+static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
+{
+       u8 data8;
+       u8 member;
+       u16 data16;
+       struct ksz_port *p = &dev->ports[port];
+
+       /* enable tag tail for host port */
+       if (cpu_port)
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
+                            true);
+
+       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
+
+       /* set back pressure */
+       ksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
+
+       /* enable broadcast storm limit */
+       ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
+
+       /* disable DiffServ priority */
+       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
+
+       /* replace priority */
+       ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
+                    false);
+       ksz9477_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
+                          MTI_PVID_REPLACE, false);
+
+       /* enable 802.1p priority */
+       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
+
+       if (port < dev->phy_port_cnt) {
+               /* do not force flow control */
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
+                            PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
+                            false);
+
+       } else {
+               /* force flow control */
+               ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
+                            PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL,
+                            true);
+
+               /* configure MAC to 1G & RGMII mode */
+               ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
+               data8 &= ~PORT_MII_NOT_1GBIT;
+               data8 &= ~PORT_MII_SEL_M;
+               switch (dev->interface) {
+               case PHY_INTERFACE_MODE_MII:
+                       data8 |= PORT_MII_NOT_1GBIT;
+                       data8 |= PORT_MII_SEL;
+                       p->phydev.speed = SPEED_100;
+                       break;
+               case PHY_INTERFACE_MODE_RMII:
+                       data8 |= PORT_MII_NOT_1GBIT;
+                       data8 |= PORT_RMII_SEL;
+                       p->phydev.speed = SPEED_100;
+                       break;
+               case PHY_INTERFACE_MODE_GMII:
+                       data8 |= PORT_GMII_SEL;
+                       p->phydev.speed = SPEED_1000;
+                       break;
+               default:
+                       data8 &= ~PORT_RGMII_ID_IG_ENABLE;
+                       data8 &= ~PORT_RGMII_ID_EG_ENABLE;
+                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                           dev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+                               data8 |= PORT_RGMII_ID_IG_ENABLE;
+                       if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+                           dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+                               data8 |= PORT_RGMII_ID_EG_ENABLE;
+                       data8 |= PORT_RGMII_SEL;
+                       p->phydev.speed = SPEED_1000;
+                       break;
+               }
+               ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
+               p->phydev.duplex = 1;
+       }
+       if (cpu_port) {
+               member = dev->port_mask;
+               dev->on_ports = dev->host_mask;
+               dev->live_ports = dev->host_mask;
+       } else {
+               member = dev->host_mask | p->vid_member;
+               dev->on_ports |= (1 << port);
+
+               /* Link was detected before port is enabled. */
+               if (p->phydev.link)
+                       dev->live_ports |= (1 << port);
+       }
+       ksz9477_cfg_port_member(dev, port, member);
+
+       /* clear pending interrupts */
+       if (port < dev->phy_port_cnt)
+               ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
+}
+
+static void ksz9477_config_cpu_port(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p;
+       int i;
+
+       ds->num_ports = dev->port_cnt;
+
+       for (i = 0; i < dev->port_cnt; i++) {
+               if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
+                       dev->cpu_port = i;
+                       dev->host_mask = (1 << dev->cpu_port);
+                       dev->port_mask |= dev->host_mask;
+
+                       /* enable cpu port */
+                       ksz9477_port_setup(dev, i, true);
+                       p = &dev->ports[dev->cpu_port];
+                       p->vid_member = dev->port_mask;
+                       p->on = 1;
+               }
+       }
+
+       dev->member = dev->host_mask;
+
+       for (i = 0; i < dev->mib_port_cnt; i++) {
+               if (i == dev->cpu_port)
+                       continue;
+               p = &dev->ports[i];
+
+               /* Initialize to non-zero so that ksz_cfg_port_member() will
+                * be called.
+                */
+               p->vid_member = (1 << i);
+               p->member = dev->port_mask;
+               ksz9477_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+               p->on = 1;
+               if (i < dev->phy_port_cnt)
+                       p->phy = 1;
+               if (dev->chip_id == 0x00947700 && i == 6) {
+                       p->sgmii = 1;
+
+                       /* SGMII PHY detection code is not implemented yet. */
+                       p->phy = 0;
+               }
+       }
+}
+
+static int ksz9477_setup(struct dsa_switch *ds)
+{
+       struct ksz_device *dev = ds->priv;
+       int ret = 0;
+
+       dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+                                      dev->num_vlans, GFP_KERNEL);
+       if (!dev->vlan_cache)
+               return -ENOMEM;
+
+       ret = ksz9477_reset_switch(dev);
+       if (ret) {
+               dev_err(ds->dev, "failed to reset switch\n");
+               return ret;
+       }
+
+       /* accept packet up to 2000bytes */
+       ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
+
+       ksz9477_config_cpu_port(ds);
+
+       ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);
+
+       /* queue based egress rate limit */
+       ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
+
+       /* start switch */
+       ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
+
+       return 0;
+}
+
+static const struct dsa_switch_ops ksz9477_switch_ops = {
+       .get_tag_protocol       = ksz9477_get_tag_protocol,
+       .setup                  = ksz9477_setup,
+       .phy_read               = ksz9477_phy_read16,
+       .phy_write              = ksz9477_phy_write16,
+       .port_enable            = ksz_enable_port,
+       .port_disable           = ksz_disable_port,
+       .get_strings            = ksz9477_get_strings,
+       .get_ethtool_stats      = ksz_get_ethtool_stats,
+       .get_sset_count         = ksz_sset_count,
+       .port_bridge_join       = ksz_port_bridge_join,
+       .port_bridge_leave      = ksz_port_bridge_leave,
+       .port_stp_state_set     = ksz9477_port_stp_state_set,
+       .port_fast_age          = ksz_port_fast_age,
+       .port_vlan_filtering    = ksz9477_port_vlan_filtering,
+       .port_vlan_prepare      = ksz_port_vlan_prepare,
+       .port_vlan_add          = ksz9477_port_vlan_add,
+       .port_vlan_del          = ksz9477_port_vlan_del,
+       .port_fdb_dump          = ksz9477_port_fdb_dump,
+       .port_fdb_add           = ksz9477_port_fdb_add,
+       .port_fdb_del           = ksz9477_port_fdb_del,
+       .port_mdb_prepare       = ksz_port_mdb_prepare,
+       .port_mdb_add           = ksz9477_port_mdb_add,
+       .port_mdb_del           = ksz9477_port_mdb_del,
+       .port_mirror_add        = ksz9477_port_mirror_add,
+       .port_mirror_del        = ksz9477_port_mirror_del,
+};
+
+static u32 ksz9477_get_port_addr(int port, int offset)
+{
+       return PORT_CTRL_ADDR(port, offset);
+}
+
+static int ksz9477_switch_detect(struct ksz_device *dev)
+{
+       u8 data8;
+       u32 id32;
+       int ret;
+
+       /* turn off SPI DO Edge select */
+       ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
+       if (ret)
+               return ret;
+
+       data8 &= ~SPI_AUTO_EDGE_DETECTION;
+       ret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
+       if (ret)
+               return ret;
+
+       /* read chip id */
+       ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
+       if (ret)
+               return ret;
+
+       /* Number of ports can be reduced depending on chip. */
+       dev->mib_port_cnt = TOTAL_PORT_NUM;
+       dev->phy_port_cnt = 5;
+
+       dev->chip_id = id32;
+
+       return 0;
+}
+
+struct ksz_chip_data {
+       u32 chip_id;
+       const char *dev_name;
+       int num_vlans;
+       int num_alus;
+       int num_statics;
+       int cpu_ports;
+       int port_cnt;
+};
+
+static const struct ksz_chip_data ksz9477_switch_chips[] = {
+       {
+               .chip_id = 0x00947700,
+               .dev_name = "KSZ9477",
+               .num_vlans = 4096,
+               .num_alus = 4096,
+               .num_statics = 16,
+               .cpu_ports = 0x7F,      /* can be configured as cpu port */
+               .port_cnt = 7,          /* total physical port count */
+       },
+       {
+               .chip_id = 0x00989700,
+               .dev_name = "KSZ9897",
+               .num_vlans = 4096,
+               .num_alus = 4096,
+               .num_statics = 16,
+               .cpu_ports = 0x7F,      /* can be configured as cpu port */
+               .port_cnt = 7,          /* total physical port count */
+       },
+};
+
+static int ksz9477_switch_init(struct ksz_device *dev)
+{
+       int i;
+
+       dev->ds->ops = &ksz9477_switch_ops;
+
+       for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) {
+               const struct ksz_chip_data *chip = &ksz9477_switch_chips[i];
+
+               if (dev->chip_id == chip->chip_id) {
+                       dev->name = chip->dev_name;
+                       dev->num_vlans = chip->num_vlans;
+                       dev->num_alus = chip->num_alus;
+                       dev->num_statics = chip->num_statics;
+                       dev->port_cnt = chip->port_cnt;
+                       dev->cpu_ports = chip->cpu_ports;
+
+                       break;
+               }
+       }
+
+       /* no switch found */
+       if (!dev->port_cnt)
+               return -ENODEV;
+
+       dev->port_mask = (1 << dev->port_cnt) - 1;
+
+       dev->reg_mib_cnt = SWITCH_COUNTER_NUM;
+       dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM;
+
+       i = dev->mib_port_cnt;
+       dev->ports = devm_kzalloc(dev->dev, sizeof(struct ksz_port) * i,
+                                 GFP_KERNEL);
+       if (!dev->ports)
+               return -ENOMEM;
+       for (i = 0; i < dev->mib_port_cnt; i++) {
+               dev->ports[i].mib.counters =
+                       devm_kzalloc(dev->dev,
+                                    sizeof(u64) *
+                                    (TOTAL_SWITCH_COUNTER_NUM + 1),
+                                    GFP_KERNEL);
+               if (!dev->ports[i].mib.counters)
+                       return -ENOMEM;
+       }
+       dev->interface = PHY_INTERFACE_MODE_RGMII_TXID;
+
+       return 0;
+}
+
+static void ksz9477_switch_exit(struct ksz_device *dev)
+{
+       ksz9477_reset_switch(dev);
+}
+
+static const struct ksz_dev_ops ksz9477_dev_ops = {
+       .get_port_addr = ksz9477_get_port_addr,
+       .cfg_port_member = ksz9477_cfg_port_member,
+       .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table,
+       .port_setup = ksz9477_port_setup,
+       .shutdown = ksz9477_reset_switch,
+       .detect = ksz9477_switch_detect,
+       .init = ksz9477_switch_init,
+       .exit = ksz9477_switch_exit,
+};
+
+int ksz9477_switch_register(struct ksz_device *dev)
+{
+       return ksz_switch_register(dev, &ksz9477_dev_ops);
+}
+EXPORT_SYMBOL(ksz9477_switch_register);
+
+MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
+MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch DSA Driver");
+MODULE_LICENSE("GPL");
similarity index 98%
rename from drivers/net/dsa/microchip/ksz_9477_reg.h
rename to drivers/net/dsa/microchip/ksz9477_reg.h
index 6aa6752..2938e89 100644 (file)
@@ -1,19 +1,8 @@
-/*
- * Microchip KSZ9477 register definitions
- *
- * Copyright (C) 2017
+/* SPDX-License-Identifier: GPL-2.0
  *
- * 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.
+ * Microchip KSZ9477 register definitions
  *
- * 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.
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
  */
 
 #ifndef __KSZ9477_REGS_H
diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c
new file mode 100644 (file)
index 0000000..d757ba1
--- /dev/null
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip KSZ9477 series register access through SPI
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "ksz_priv.h"
+#include "ksz_spi.h"
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD                    3
+#define KS_SPIOP_WR                    2
+
+#define SPI_ADDR_SHIFT                 24
+#define SPI_ADDR_MASK                  (BIT(SPI_ADDR_SHIFT) - 1)
+#define SPI_TURNAROUND_SHIFT           5
+
+/* Enough to read all switch port registers. */
+#define SPI_TX_BUF_LEN                 0x100
+
+static int ksz9477_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
+                               unsigned int len)
+{
+       u32 txbuf;
+       int ret;
+
+       txbuf = reg & SPI_ADDR_MASK;
+       txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
+       txbuf <<= SPI_TURNAROUND_SHIFT;
+       txbuf = cpu_to_be32(txbuf);
+
+       ret = spi_write_then_read(spi, &txbuf, 4, val, len);
+       return ret;
+}
+
+static int ksz9477_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
+                                unsigned int len)
+{
+       u32 *txbuf = (u32 *)val;
+
+       *txbuf = reg & SPI_ADDR_MASK;
+       *txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
+       *txbuf <<= SPI_TURNAROUND_SHIFT;
+       *txbuf = cpu_to_be32(*txbuf);
+
+       return spi_write(spi, txbuf, 4 + len);
+}
+
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+                       unsigned int len)
+{
+       struct spi_device *spi = dev->priv;
+
+       return ksz9477_spi_read_reg(spi, reg, data, len);
+}
+
+static int ksz_spi_write(struct ksz_device *dev, u32 reg, void *data,
+                        unsigned int len)
+{
+       struct spi_device *spi = dev->priv;
+
+       if (len > SPI_TX_BUF_LEN)
+               len = SPI_TX_BUF_LEN;
+       memcpy(&dev->txbuf[4], data, len);
+       return ksz9477_spi_write_reg(spi, reg, dev->txbuf, len);
+}
+
+static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       *val = 0;
+       ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
+       if (!ret) {
+               *val = be32_to_cpu(*val);
+               /* convert to 24bit */
+               *val >>= 8;
+       }
+
+       return ret;
+}
+
+static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+       /* make it to big endian 24bit from MSB */
+       value <<= 8;
+       value = cpu_to_be32(value);
+       return ksz_spi_write(dev, reg, &value, 3);
+}
+
+static const struct ksz_io_ops ksz9477_spi_ops = {
+       .read8 = ksz_spi_read8,
+       .read16 = ksz_spi_read16,
+       .read24 = ksz_spi_read24,
+       .read32 = ksz_spi_read32,
+       .write8 = ksz_spi_write8,
+       .write16 = ksz_spi_write16,
+       .write24 = ksz_spi_write24,
+       .write32 = ksz_spi_write32,
+       .get = ksz_spi_get,
+       .set = ksz_spi_set,
+};
+
+static int ksz9477_spi_probe(struct spi_device *spi)
+{
+       struct ksz_device *dev;
+       int ret;
+
+       dev = ksz_switch_alloc(&spi->dev, &ksz9477_spi_ops, spi);
+       if (!dev)
+               return -ENOMEM;
+
+       if (spi->dev.platform_data)
+               dev->pdata = spi->dev.platform_data;
+
+       dev->txbuf = devm_kzalloc(dev->dev, 4 + SPI_TX_BUF_LEN, GFP_KERNEL);
+
+       ret = ksz9477_switch_register(dev);
+
+       /* Main DSA driver may not be started yet. */
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, dev);
+
+       return 0;
+}
+
+static int ksz9477_spi_remove(struct spi_device *spi)
+{
+       struct ksz_device *dev = spi_get_drvdata(spi);
+
+       if (dev)
+               ksz_switch_remove(dev);
+
+       return 0;
+}
+
+static void ksz9477_spi_shutdown(struct spi_device *spi)
+{
+       struct ksz_device *dev = spi_get_drvdata(spi);
+
+       if (dev && dev->dev_ops->shutdown)
+               dev->dev_ops->shutdown(dev);
+}
+
+static const struct of_device_id ksz9477_dt_ids[] = {
+       { .compatible = "microchip,ksz9477" },
+       { .compatible = "microchip,ksz9897" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
+
+static struct spi_driver ksz9477_spi_driver = {
+       .driver = {
+               .name   = "ksz9477-switch",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(ksz9477_dt_ids),
+       },
+       .probe  = ksz9477_spi_probe,
+       .remove = ksz9477_spi_remove,
+       .shutdown = ksz9477_spi_shutdown,
+};
+
+module_spi_driver(ksz9477_spi_driver);
+
+MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
+MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch SPI access Driver");
+MODULE_LICENSE("GPL");
index 86b6464..9705808 100644 (file)
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Microchip switch driver main logic
  *
- * Copyright (C) 2017
- *
- * 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.
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
  */
 
 #include <linux/delay.h>
 #include <linux/phy.h>
 #include <linux/etherdevice.h>
 #include <linux/if_bridge.h>
+#include <linux/of_net.h>
 #include <net/dsa.h>
 #include <net/switchdev.h>
 
 #include "ksz_priv.h"
 
-static const struct {
-       int index;
-       char string[ETH_GSTRING_LEN];
-} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {
-       { 0x00, "rx_hi" },
-       { 0x01, "rx_undersize" },
-       { 0x02, "rx_fragments" },
-       { 0x03, "rx_oversize" },
-       { 0x04, "rx_jabbers" },
-       { 0x05, "rx_symbol_err" },
-       { 0x06, "rx_crc_err" },
-       { 0x07, "rx_align_err" },
-       { 0x08, "rx_mac_ctrl" },
-       { 0x09, "rx_pause" },
-       { 0x0A, "rx_bcast" },
-       { 0x0B, "rx_mcast" },
-       { 0x0C, "rx_ucast" },
-       { 0x0D, "rx_64_or_less" },
-       { 0x0E, "rx_65_127" },
-       { 0x0F, "rx_128_255" },
-       { 0x10, "rx_256_511" },
-       { 0x11, "rx_512_1023" },
-       { 0x12, "rx_1024_1522" },
-       { 0x13, "rx_1523_2000" },
-       { 0x14, "rx_2001" },
-       { 0x15, "tx_hi" },
-       { 0x16, "tx_late_col" },
-       { 0x17, "tx_pause" },
-       { 0x18, "tx_bcast" },
-       { 0x19, "tx_mcast" },
-       { 0x1A, "tx_ucast" },
-       { 0x1B, "tx_deferred" },
-       { 0x1C, "tx_total_col" },
-       { 0x1D, "tx_exc_col" },
-       { 0x1E, "tx_single_col" },
-       { 0x1F, "tx_mult_col" },
-       { 0x80, "rx_total" },
-       { 0x81, "tx_total" },
-       { 0x82, "rx_discards" },
-       { 0x83, "tx_discards" },
-};
-
-static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
-{
-       u8 data;
-
-       ksz_read8(dev, addr, &data);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       ksz_write8(dev, addr, data);
-}
-
-static void ksz_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool set)
-{
-       u32 data;
-
-       ksz_read32(dev, addr, &data);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       ksz_write32(dev, addr, data);
-}
-
-static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
-                        bool set)
-{
-       u32 addr;
-       u8 data;
-
-       addr = PORT_CTRL_ADDR(port, offset);
-       ksz_read8(dev, addr, &data);
-
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-
-       ksz_write8(dev, addr, data);
-}
-
-static void ksz_port_cfg32(struct ksz_device *dev, int port, int offset,
-                          u32 bits, bool set)
-{
-       u32 addr;
-       u32 data;
-
-       addr = PORT_CTRL_ADDR(port, offset);
-       ksz_read32(dev, addr, &data);
-
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-
-       ksz_write32(dev, addr, data);
-}
-
-static int wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton, int timeout)
-{
-       u8 data;
-
-       do {
-               ksz_read8(dev, REG_SW_VLAN_CTRL, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int get_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret;
-
-       mutex_lock(&dev->vlan_mutex);
-
-       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
-       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);
-
-       /* wait to be cleared */
-       ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read vlan table\n");
-               goto exit;
-       }
-
-       ksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);
-       ksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);
-       ksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);
-
-       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
-
-exit:
-       mutex_unlock(&dev->vlan_mutex);
-
-       return ret;
-}
-
-static int set_vlan_table(struct dsa_switch *ds, u16 vid, u32 *vlan_table)
-{
-       struct ksz_device *dev = ds->priv;
-       int ret;
-
-       mutex_lock(&dev->vlan_mutex);
-
-       ksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);
-       ksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);
-       ksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);
-
-       ksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);
-       ksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);
-
-       /* wait to be cleared */
-       ret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to write vlan table\n");
-               goto exit;
-       }
-
-       ksz_write8(dev, REG_SW_VLAN_CTRL, 0);
-
-       /* update vlan cache table */
-       dev->vlan_cache[vid].table[0] = vlan_table[0];
-       dev->vlan_cache[vid].table[1] = vlan_table[1];
-       dev->vlan_cache[vid].table[2] = vlan_table[2];
-
-exit:
-       mutex_unlock(&dev->vlan_mutex);
-
-       return ret;
-}
-
-static void read_table(struct dsa_switch *ds, u32 *table)
-{
-       struct ksz_device *dev = ds->priv;
-
-       ksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);
-       ksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);
-       ksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);
-       ksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]);
-}
-
-static void write_table(struct dsa_switch *ds, u32 *table)
-{
-       struct ksz_device *dev = ds->priv;
-
-       ksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);
-       ksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);
-       ksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);
-       ksz_write32(dev, REG_SW_ALU_VAL_D, table[3]);
-}
-
-static int wait_alu_ready(struct ksz_device *dev, u32 waiton, int timeout)
-{
-       u32 data;
-
-       do {
-               ksz_read32(dev, REG_SW_ALU_CTRL__4, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int wait_alu_sta_ready(struct ksz_device *dev, u32 waiton, int timeout)
+void ksz_update_port_member(struct ksz_device *dev, int port)
 {
-       u32 data;
-
-       do {
-               ksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);
-               if (!(data & waiton))
-                       break;
-               usleep_range(1, 10);
-       } while (timeout-- > 0);
-
-       if (timeout <= 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int ksz_reset_switch(struct dsa_switch *ds)
-{
-       struct ksz_device *dev = ds->priv;
-       u8 data8;
-       u16 data16;
-       u32 data32;
-
-       /* reset switch */
-       ksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);
-
-       /* turn off SPI DO Edge select */
-       ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
-       data8 &= ~SPI_AUTO_EDGE_DETECTION;
-       ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
-
-       /* default configuration */
-       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
-       data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |
-             SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
-
-       /* disable interrupts */
-       ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);
-       ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);
-       ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);
-
-       /* set broadcast storm protection 10% rate */
-       ksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);
-       data16 &= ~BROADCAST_STORM_RATE;
-       data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
-       ksz_write16(dev, REG_SW_MAC_CTRL_2, data16);
-
-       return 0;
-}
-
-static void port_setup(struct ksz_device *dev, int port, bool cpu_port)
-{
-       u8 data8;
-       u16 data16;
-
-       /* enable tag tail for host port */
-       if (cpu_port)
-               ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,
-                            true);
-
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);
-
-       /* set back pressure */
-       ksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, true);
-
-       /* set flow control */
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0,
-                    PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true);
-
-       /* enable broadcast storm limit */
-       ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
-
-       /* disable DiffServ priority */
-       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, false);
-
-       /* replace priority */
-       ksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,
-                    false);
-       ksz_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,
-                      MTI_PVID_REPLACE, false);
-
-       /* enable 802.1p priority */
-       ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);
-
-       /* configure MAC to 1G & RGMII mode */
-       ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);
-       data8 |= PORT_RGMII_ID_EG_ENABLE;
-       data8 &= ~PORT_MII_NOT_1GBIT;
-       data8 &= ~PORT_MII_SEL_M;
-       data8 |= PORT_RGMII_SEL;
-       ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);
-
-       /* clear pending interrupts */
-       ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16);
-}
-
-static void ksz_config_cpu_port(struct dsa_switch *ds)
-{
-       struct ksz_device *dev = ds->priv;
+       struct ksz_port *p;
        int i;
 
-       ds->num_ports = dev->port_cnt;
-
-       for (i = 0; i < ds->num_ports; i++) {
-               if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {
-                       dev->cpu_port = i;
-
-                       /* enable cpu port */
-                       port_setup(dev, i, true);
-               }
+       for (i = 0; i < dev->port_cnt; i++) {
+               if (i == port || i == dev->cpu_port)
+                       continue;
+               p = &dev->ports[i];
+               if (!(dev->member & (1 << i)))
+                       continue;
+
+               /* Port is a member of the bridge and is forwarding. */
+               if (p->stp_state == BR_STATE_FORWARDING &&
+                   p->member != dev->member)
+                       dev->dev_ops->cfg_port_member(dev, i, dev->member);
        }
 }
+EXPORT_SYMBOL_GPL(ksz_update_port_member);
 
-static int ksz_setup(struct dsa_switch *ds)
+int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
 {
        struct ksz_device *dev = ds->priv;
-       int ret = 0;
-
-       dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
-                                      dev->num_vlans, GFP_KERNEL);
-       if (!dev->vlan_cache)
-               return -ENOMEM;
+       u16 val = 0xffff;
 
-       ret = ksz_reset_switch(ds);
-       if (ret) {
-               dev_err(ds->dev, "failed to reset switch\n");
-               return ret;
-       }
-
-       /* accept packet up to 2000bytes */
-       ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);
-
-       ksz_config_cpu_port(ds);
-
-       ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);
-
-       /* queue based egress rate limit */
-       ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
-
-       /* start switch */
-       ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
-
-       return 0;
-}
-
-static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
-                                                 int port)
-{
-       return DSA_TAG_PROTO_KSZ;
-}
-
-static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
-{
-       struct ksz_device *dev = ds->priv;
-       u16 val = 0;
-
-       ksz_pread16(dev, addr, 0x100 + (reg << 1), &val);
+       dev->dev_ops->r_phy(dev, addr, reg, &val);
 
        return val;
 }
+EXPORT_SYMBOL_GPL(ksz_phy_read16);
 
-static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
-{
-       struct ksz_device *dev = ds->priv;
-
-       ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);
-
-       return 0;
-}
-
-static int ksz_enable_port(struct dsa_switch *ds, int port,
-                          struct phy_device *phy)
+int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
 {
        struct ksz_device *dev = ds->priv;
 
-       /* setup slave port */
-       port_setup(dev, port, false);
+       dev->dev_ops->w_phy(dev, addr, reg, val);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_phy_write16);
 
-static void ksz_disable_port(struct dsa_switch *ds, int port,
-                            struct phy_device *phy)
+int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
 {
        struct ksz_device *dev = ds->priv;
 
-       /* there is no port disable */
-       ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true);
-}
-
-static int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
-{
        if (sset != ETH_SS_STATS)
                return 0;
 
-       return TOTAL_SWITCH_COUNTER_NUM;
+       return dev->mib_cnt;
 }
+EXPORT_SYMBOL_GPL(ksz_sset_count);
 
-static void ksz_get_strings(struct dsa_switch *ds, int port,
-                           u32 stringset, uint8_t *buf)
-{
-       int i;
-
-       if (stringset != ETH_SS_STATS)
-               return;
-
-       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
-               memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
-                      ETH_GSTRING_LEN);
-       }
-}
-
-static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
-                                 uint64_t *buf)
+int ksz_port_bridge_join(struct dsa_switch *ds, int port,
+                        struct net_device *br)
 {
        struct ksz_device *dev = ds->priv;
-       int i;
-       u32 data;
-       int timeout;
-
-       mutex_lock(&dev->stats_mutex);
-
-       for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
-               data = MIB_COUNTER_READ;
-               data |= ((mib_names[i].index & 0xFF) << MIB_COUNTER_INDEX_S);
-               ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
-
-               timeout = 1000;
-               do {
-                       ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
-                                   &data);
-                       usleep_range(1, 10);
-                       if (!(data & MIB_COUNTER_READ))
-                               break;
-               } while (timeout-- > 0);
-
-               /* failed to read MIB. get out of loop */
-               if (!timeout) {
-                       dev_dbg(dev->dev, "Failed to get MIB\n");
-                       break;
-               }
 
-               /* count resets upon read */
-               ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
+       dev->br_member |= (1 << port);
 
-               dev->mib_value[i] += (uint64_t)data;
-               buf[i] = dev->mib_value[i];
-       }
-
-       mutex_unlock(&dev->stats_mutex);
-}
-
-static void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
-       struct ksz_device *dev = ds->priv;
-       u8 data;
-
-       ksz_pread8(dev, port, P_STP_CTRL, &data);
-       data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);
-
-       switch (state) {
-       case BR_STATE_DISABLED:
-               data |= PORT_LEARN_DISABLE;
-               break;
-       case BR_STATE_LISTENING:
-               data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);
-               break;
-       case BR_STATE_LEARNING:
-               data |= PORT_RX_ENABLE;
-               break;
-       case BR_STATE_FORWARDING:
-               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
-               break;
-       case BR_STATE_BLOCKING:
-               data |= PORT_LEARN_DISABLE;
-               break;
-       default:
-               dev_err(ds->dev, "invalid STP state: %d\n", state);
-               return;
-       }
+       /* port_stp_state_set() will be called after to put the port in
+        * appropriate state so there is no need to do anything.
+        */
 
-       ksz_pwrite8(dev, port, P_STP_CTRL, data);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_bridge_join);
 
-static void ksz_port_fast_age(struct dsa_switch *ds, int port)
+void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
+                          struct net_device *br)
 {
        struct ksz_device *dev = ds->priv;
-       u8 data8;
 
-       ksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);
-       data8 |= SW_FAST_AGING;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+       dev->br_member &= ~(1 << port);
+       dev->member &= ~(1 << port);
 
-       data8 &= ~SW_FAST_AGING;
-       ksz_write8(dev, REG_SW_LUE_CTRL_1, data8);
+       /* port_stp_state_set() will be called after to put the port in
+        * forwarding state so there is no need to do anything.
+        */
 }
+EXPORT_SYMBOL_GPL(ksz_port_bridge_leave);
 
-static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)
+void ksz_port_fast_age(struct dsa_switch *ds, int port)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (flag) {
-               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
-                            PORT_VLAN_LOOKUP_VID_0, true);
-               ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true);
-               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);
-       } else {
-               ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);
-               ksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, false);
-               ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
-                            PORT_VLAN_LOOKUP_VID_0, false);
-       }
-
-       return 0;
+       dev->dev_ops->flush_dyn_mac_table(dev, port);
 }
+EXPORT_SYMBOL_GPL(ksz_port_fast_age);
 
-static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_vlan *vlan)
+int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan)
 {
        /* nothing needed */
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare);
 
-static void ksz_port_vlan_add(struct dsa_switch *ds, int port,
-                             const struct switchdev_obj_port_vlan *vlan)
-{
-       struct ksz_device *dev = ds->priv;
-       u32 vlan_table[3];
-       u16 vid;
-       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (get_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return;
-               }
-
-               vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
-               if (untagged)
-                       vlan_table[1] |= BIT(port);
-               else
-                       vlan_table[1] &= ~BIT(port);
-               vlan_table[1] &= ~(BIT(dev->cpu_port));
-
-               vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
-
-               if (set_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return;
-               }
-
-               /* change PVID */
-               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
-                       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
-       }
-}
-
-static int ksz_port_vlan_del(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_vlan *vlan)
-{
-       struct ksz_device *dev = ds->priv;
-       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-       u32 vlan_table[3];
-       u16 vid;
-       u16 pvid;
-
-       ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
-       pvid = pvid & 0xFFF;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (get_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return -ETIMEDOUT;
-               }
-
-               vlan_table[2] &= ~BIT(port);
-
-               if (pvid == vid)
-                       pvid = 1;
-
-               if (untagged)
-                       vlan_table[1] &= ~BIT(port);
-
-               if (set_vlan_table(ds, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return -ETIMEDOUT;
-               }
-       }
-
-       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
-
-       return 0;
-}
-
-struct alu_struct {
-       /* entry 1 */
-       u8      is_static:1;
-       u8      is_src_filter:1;
-       u8      is_dst_filter:1;
-       u8      prio_age:3;
-       u32     _reserv_0_1:23;
-       u8      mstp:3;
-       /* entry 2 */
-       u8      is_override:1;
-       u8      is_use_fid:1;
-       u32     _reserv_1_1:23;
-       u8      port_forward:7;
-       /* entry 3 & 4*/
-       u32     _reserv_2_1:9;
-       u8      fid:7;
-       u8      mac[ETH_ALEN];
-};
-
-static int ksz_port_fdb_add(struct dsa_switch *ds, int port,
-                           const unsigned char *addr, u16 vid)
-{
-       struct ksz_device *dev = ds->priv;
-       u32 alu_table[4];
-       u32 data;
-       int ret = 0;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* find any entry with mac & vid */
-       data = vid << ALU_FID_INDEX_S;
-       data |= ((addr[0] << 8) | addr[1]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
-
-       data = ((addr[2] << 24) | (addr[3] << 16));
-       data |= ((addr[4] << 8) | addr[5]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
-
-       /* start read operation */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read ALU\n");
-               goto exit;
-       }
-
-       /* read ALU entry */
-       read_table(ds, alu_table);
-
-       /* update ALU entry */
-       alu_table[0] = ALU_V_STATIC_VALID;
-       alu_table[1] |= BIT(port);
-       if (vid)
-               alu_table[1] |= ALU_V_USE_FID;
-       alu_table[2] = (vid << ALU_V_FID_S);
-       alu_table[2] |= ((addr[0] << 8) | addr[1]);
-       alu_table[3] = ((addr[2] << 24) | (addr[3] << 16));
-       alu_table[3] |= ((addr[4] << 8) | addr[5]);
-
-       write_table(ds, alu_table);
-
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to write ALU\n");
-
-exit:
-       mutex_unlock(&dev->alu_mutex);
-
-       return ret;
-}
-
-static int ksz_port_fdb_del(struct dsa_switch *ds, int port,
-                           const unsigned char *addr, u16 vid)
-{
-       struct ksz_device *dev = ds->priv;
-       u32 alu_table[4];
-       u32 data;
-       int ret = 0;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* read any entry with mac & vid */
-       data = vid << ALU_FID_INDEX_S;
-       data |= ((addr[0] << 8) | addr[1]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_0, data);
-
-       data = ((addr[2] << 24) | (addr[3] << 16));
-       data |= ((addr[4] << 8) | addr[5]);
-       ksz_write32(dev, REG_SW_ALU_INDEX_1, data);
-
-       /* start read operation */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0) {
-               dev_dbg(dev->dev, "Failed to read ALU\n");
-               goto exit;
-       }
-
-       ksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);
-       if (alu_table[0] & ALU_V_STATIC_VALID) {
-               ksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);
-               ksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);
-               ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
-
-               /* clear forwarding port */
-               alu_table[2] &= ~BIT(port);
-
-               /* if there is no port to forward, clear table */
-               if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
-                       alu_table[0] = 0;
-                       alu_table[1] = 0;
-                       alu_table[2] = 0;
-                       alu_table[3] = 0;
-               }
-       } else {
-               alu_table[0] = 0;
-               alu_table[1] = 0;
-               alu_table[2] = 0;
-               alu_table[3] = 0;
-       }
-
-       write_table(ds, alu_table);
-
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);
-
-       /* wait to be finished */
-       ret = wait_alu_ready(dev, ALU_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to write ALU\n");
-
-exit:
-       mutex_unlock(&dev->alu_mutex);
-
-       return ret;
-}
-
-static void convert_alu(struct alu_struct *alu, u32 *alu_table)
-{
-       alu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);
-       alu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);
-       alu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);
-       alu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &
-                       ALU_V_PRIO_AGE_CNT_M;
-       alu->mstp = alu_table[0] & ALU_V_MSTP_M;
-
-       alu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);
-       alu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);
-       alu->port_forward = alu_table[1] & ALU_V_PORT_MAP;
-
-       alu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;
-
-       alu->mac[0] = (alu_table[2] >> 8) & 0xFF;
-       alu->mac[1] = alu_table[2] & 0xFF;
-       alu->mac[2] = (alu_table[3] >> 24) & 0xFF;
-       alu->mac[3] = (alu_table[3] >> 16) & 0xFF;
-       alu->mac[4] = (alu_table[3] >> 8) & 0xFF;
-       alu->mac[5] = alu_table[3] & 0xFF;
-}
-
-static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,
-                            dsa_fdb_dump_cb_t *cb, void *data)
+int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
+                     void *data)
 {
        struct ksz_device *dev = ds->priv;
        int ret = 0;
-       u32 ksz_data;
-       u32 alu_table[4];
+       u16 i = 0;
+       u16 entries = 0;
+       u8 timestamp = 0;
+       u8 fid;
+       u8 member;
        struct alu_struct alu;
-       int timeout;
-
-       mutex_lock(&dev->alu_mutex);
-
-       /* start ALU search */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);
 
        do {
-               timeout = 1000;
-               do {
-                       ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);
-                       if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))
-                               break;
-                       usleep_range(1, 10);
-               } while (timeout-- > 0);
-
-               if (!timeout) {
-                       dev_dbg(dev->dev, "Failed to search ALU\n");
-                       ret = -ETIMEDOUT;
-                       goto exit;
-               }
-
-               /* read ALU table */
-               read_table(ds, alu_table);
-
-               convert_alu(&alu, alu_table);
-
-               if (alu.port_forward & BIT(port)) {
+               alu.is_static = false;
+               ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid,
+                                                   &member, &timestamp,
+                                                   &entries);
+               if (!ret && (member & BIT(port))) {
                        ret = cb(alu.mac, alu.fid, alu.is_static, data);
                        if (ret)
-                               goto exit;
+                               break;
                }
-       } while (ksz_data & ALU_START);
-
-exit:
-
-       /* stop ALU search */
-       ksz_write32(dev, REG_SW_ALU_CTRL__4, 0);
-
-       mutex_unlock(&dev->alu_mutex);
+               i++;
+       } while (i < entries);
+       if (i >= entries)
+               ret = 0;
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(ksz_port_fdb_dump);
 
-static int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_mdb *mdb)
+int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
+                        const struct switchdev_obj_port_mdb *mdb)
 {
        /* nothing to do */
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare);
 
-static void ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_mdb *mdb)
+void ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                     const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
-       u32 static_table[4];
-       u32 data;
+       struct alu_struct alu;
        int index;
-       u32 mac_hi, mac_lo;
-
-       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
-       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
-       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
-
-       mutex_lock(&dev->alu_mutex);
+       int empty = 0;
 
+       alu.port_forward = 0;
        for (index = 0; index < dev->num_statics; index++) {
-               /* find empty slot first */
-               data = (index << ALU_STAT_INDEX_S) |
-                       ALU_STAT_READ | ALU_STAT_START;
-               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-               /* wait to be finished */
-               if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {
-                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
-                       goto exit;
-               }
-
-               /* read ALU static table */
-               read_table(ds, static_table);
-
-               if (static_table[0] & ALU_V_STATIC_VALID) {
-                       /* check this has same vid & mac address */
-                       if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
-                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
-                           (static_table[3] == mac_lo)) {
-                               /* found matching one */
+               if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
+                       /* Found one already in static MAC table. */
+                       if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
+                           alu.fid == mdb->vid)
                                break;
-                       }
-               } else {
-                       /* found empty one */
-                       break;
+               /* Remember the first empty entry. */
+               } else if (!empty) {
+                       empty = index + 1;
                }
        }
 
        /* no available entry */
-       if (index == dev->num_statics)
-               goto exit;
+       if (index == dev->num_statics && !empty)
+               return;
 
        /* add entry */
-       static_table[0] = ALU_V_STATIC_VALID;
-       static_table[1] |= BIT(port);
-       if (mdb->vid)
-               static_table[1] |= ALU_V_USE_FID;
-       static_table[2] = (mdb->vid << ALU_V_FID_S);
-       static_table[2] |= mac_hi;
-       static_table[3] = mac_lo;
-
-       write_table(ds, static_table);
-
-       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
-       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-       /* wait to be finished */
-       if (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)
-               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+       if (index == dev->num_statics) {
+               index = empty - 1;
+               memset(&alu, 0, sizeof(alu));
+               memcpy(alu.mac, mdb->addr, ETH_ALEN);
+               alu.is_static = true;
+       }
+       alu.port_forward |= BIT(port);
+       if (mdb->vid) {
+               alu.is_use_fid = true;
 
-exit:
-       mutex_unlock(&dev->alu_mutex);
+               /* Need a way to map VID to FID. */
+               alu.fid = mdb->vid;
+       }
+       dev->dev_ops->w_sta_mac_table(dev, index, &alu);
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_add);
 
-static int ksz_port_mdb_del(struct dsa_switch *ds, int port,
-                           const struct switchdev_obj_port_mdb *mdb)
+int ksz_port_mdb_del(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
-       u32 static_table[4];
-       u32 data;
+       struct alu_struct alu;
        int index;
        int ret = 0;
-       u32 mac_hi, mac_lo;
-
-       mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
-       mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
-       mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);
-
-       mutex_lock(&dev->alu_mutex);
 
        for (index = 0; index < dev->num_statics; index++) {
-               /* find empty slot first */
-               data = (index << ALU_STAT_INDEX_S) |
-                       ALU_STAT_READ | ALU_STAT_START;
-               ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-               /* wait to be finished */
-               ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
-               if (ret < 0) {
-                       dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
-                       goto exit;
-               }
-
-               /* read ALU static table */
-               read_table(ds, static_table);
-
-               if (static_table[0] & ALU_V_STATIC_VALID) {
-                       /* check this has same vid & mac address */
-
-                       if (((static_table[2] >> ALU_V_FID_S) == (mdb->vid)) &&
-                           ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&
-                           (static_table[3] == mac_lo)) {
-                               /* found matching one */
+               if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) {
+                       /* Found one already in static MAC table. */
+                       if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&
+                           alu.fid == mdb->vid)
                                break;
-                       }
                }
        }
 
        /* no available entry */
-       if (index == dev->num_statics) {
-               ret = -EINVAL;
+       if (index == dev->num_statics)
                goto exit;
-       }
 
        /* clear port */
-       static_table[1] &= ~BIT(port);
-
-       if ((static_table[1] & ALU_V_PORT_MAP) == 0) {
-               /* delete entry */
-               static_table[0] = 0;
-               static_table[1] = 0;
-               static_table[2] = 0;
-               static_table[3] = 0;
-       }
-
-       write_table(ds, static_table);
-
-       data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;
-       ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-
-       /* wait to be finished */
-       ret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);
-       if (ret < 0)
-               dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
+       alu.port_forward &= ~BIT(port);
+       if (!alu.port_forward)
+               alu.is_static = false;
+       dev->dev_ops->w_sta_mac_table(dev, index, &alu);
 
 exit:
-       mutex_unlock(&dev->alu_mutex);
-
        return ret;
 }
+EXPORT_SYMBOL_GPL(ksz_port_mdb_del);
 
-static int ksz_port_mirror_add(struct dsa_switch *ds, int port,
-                              struct dsa_mall_mirror_tc_entry *mirror,
-                              bool ingress)
+int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (ingress)
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
-       else
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
-
-       ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
-
-       /* configure mirror port */
-       ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
-                    PORT_MIRROR_SNIFFER, true);
+       /* setup slave port */
+       dev->dev_ops->port_setup(dev, port, false);
 
-       ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+       /* port_stp_state_set() will be called after to enable the port so
+        * there is no need to do anything.
+        */
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ksz_enable_port);
 
-static void ksz_port_mirror_del(struct dsa_switch *ds, int port,
-                               struct dsa_mall_mirror_tc_entry *mirror)
+void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy)
 {
        struct ksz_device *dev = ds->priv;
-       u8 data;
-
-       if (mirror->ingress)
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
-       else
-               ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
-
-       ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
-
-       if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))
-               ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
-                            PORT_MIRROR_SNIFFER, false);
-}
-
-static const struct dsa_switch_ops ksz_switch_ops = {
-       .get_tag_protocol       = ksz_get_tag_protocol,
-       .setup                  = ksz_setup,
-       .phy_read               = ksz_phy_read16,
-       .phy_write              = ksz_phy_write16,
-       .port_enable            = ksz_enable_port,
-       .port_disable           = ksz_disable_port,
-       .get_strings            = ksz_get_strings,
-       .get_ethtool_stats      = ksz_get_ethtool_stats,
-       .get_sset_count         = ksz_sset_count,
-       .port_stp_state_set     = ksz_port_stp_state_set,
-       .port_fast_age          = ksz_port_fast_age,
-       .port_vlan_filtering    = ksz_port_vlan_filtering,
-       .port_vlan_prepare      = ksz_port_vlan_prepare,
-       .port_vlan_add          = ksz_port_vlan_add,
-       .port_vlan_del          = ksz_port_vlan_del,
-       .port_fdb_dump          = ksz_port_fdb_dump,
-       .port_fdb_add           = ksz_port_fdb_add,
-       .port_fdb_del           = ksz_port_fdb_del,
-       .port_mdb_prepare       = ksz_port_mdb_prepare,
-       .port_mdb_add           = ksz_port_mdb_add,
-       .port_mdb_del           = ksz_port_mdb_del,
-       .port_mirror_add        = ksz_port_mirror_add,
-       .port_mirror_del        = ksz_port_mirror_del,
-};
-
-struct ksz_chip_data {
-       u32 chip_id;
-       const char *dev_name;
-       int num_vlans;
-       int num_alus;
-       int num_statics;
-       int cpu_ports;
-       int port_cnt;
-};
-
-static const struct ksz_chip_data ksz_switch_chips[] = {
-       {
-               .chip_id = 0x00947700,
-               .dev_name = "KSZ9477",
-               .num_vlans = 4096,
-               .num_alus = 4096,
-               .num_statics = 16,
-               .cpu_ports = 0x7F,      /* can be configured as cpu port */
-               .port_cnt = 7,          /* total physical port count */
-       },
-       {
-               .chip_id = 0x00989700,
-               .dev_name = "KSZ9897",
-               .num_vlans = 4096,
-               .num_alus = 4096,
-               .num_statics = 16,
-               .cpu_ports = 0x7F,      /* can be configured as cpu port */
-               .port_cnt = 7,          /* total physical port count */
-       },
-};
-
-static int ksz_switch_init(struct ksz_device *dev)
-{
-       int i;
-
-       dev->ds->ops = &ksz_switch_ops;
-
-       for (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) {
-               const struct ksz_chip_data *chip = &ksz_switch_chips[i];
 
-               if (dev->chip_id == chip->chip_id) {
-                       dev->name = chip->dev_name;
-                       dev->num_vlans = chip->num_vlans;
-                       dev->num_alus = chip->num_alus;
-                       dev->num_statics = chip->num_statics;
-                       dev->port_cnt = chip->port_cnt;
-                       dev->cpu_ports = chip->cpu_ports;
-
-                       break;
-               }
-       }
+       dev->on_ports &= ~(1 << port);
+       dev->live_ports &= ~(1 << port);
 
-       /* no switch found */
-       if (!dev->port_cnt)
-               return -ENODEV;
-
-       return 0;
+       /* port_stp_state_set() will be called after to disable the port so
+        * there is no need to do anything.
+        */
 }
+EXPORT_SYMBOL_GPL(ksz_disable_port);
 
 struct ksz_device *ksz_switch_alloc(struct device *base,
                                    const struct ksz_io_ops *ops,
@@ -1167,34 +286,8 @@ struct ksz_device *ksz_switch_alloc(struct device *base,
 }
 EXPORT_SYMBOL(ksz_switch_alloc);
 
-int ksz_switch_detect(struct ksz_device *dev)
-{
-       u8 data8;
-       u32 id32;
-       int ret;
-
-       /* turn off SPI DO Edge select */
-       ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);
-       if (ret)
-               return ret;
-
-       data8 &= ~SPI_AUTO_EDGE_DETECTION;
-       ret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);
-       if (ret)
-               return ret;
-
-       /* read chip id */
-       ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);
-       if (ret)
-               return ret;
-
-       dev->chip_id = id32;
-
-       return 0;
-}
-EXPORT_SYMBOL(ksz_switch_detect);
-
-int ksz_switch_register(struct ksz_device *dev)
+int ksz_switch_register(struct ksz_device *dev,
+                       const struct ksz_dev_ops *ops)
 {
        int ret;
 
@@ -1206,19 +299,35 @@ int ksz_switch_register(struct ksz_device *dev)
        mutex_init(&dev->alu_mutex);
        mutex_init(&dev->vlan_mutex);
 
-       if (ksz_switch_detect(dev))
+       dev->dev_ops = ops;
+
+       if (dev->dev_ops->detect(dev))
                return -EINVAL;
 
-       ret = ksz_switch_init(dev);
+       ret = dev->dev_ops->init(dev);
        if (ret)
                return ret;
 
-       return dsa_register_switch(dev->ds);
+       dev->interface = PHY_INTERFACE_MODE_MII;
+       if (dev->dev->of_node) {
+               ret = of_get_phy_mode(dev->dev->of_node);
+               if (ret >= 0)
+                       dev->interface = ret;
+       }
+
+       ret = dsa_register_switch(dev->ds);
+       if (ret) {
+               dev->dev_ops->exit(dev);
+               return ret;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(ksz_switch_register);
 
 void ksz_switch_remove(struct ksz_device *dev)
 {
+       dev->dev_ops->exit(dev);
        dsa_unregister_switch(dev->ds);
 }
 EXPORT_SYMBOL(ksz_switch_remove);
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
new file mode 100644 (file)
index 0000000..2dd832d
--- /dev/null
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Microchip switch driver common header
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ_COMMON_H
+#define __KSZ_COMMON_H
+
+void ksz_update_port_member(struct ksz_device *dev, int port);
+
+/* Common DSA access functions */
+
+int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg);
+int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val);
+int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
+int ksz_port_bridge_join(struct dsa_switch *ds, int port,
+                        struct net_device *br);
+void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
+                          struct net_device *br);
+void ksz_port_fast_age(struct dsa_switch *ds, int port);
+int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan);
+int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
+                     void *data);
+int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
+                        const struct switchdev_obj_port_mdb *mdb);
+void ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                     const struct switchdev_obj_port_mdb *mdb);
+int ksz_port_mdb_del(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb);
+int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
+void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
+
+/* Common register access functions */
+
+static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read8(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read16(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read24(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read24(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read32(dev, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write8(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write16(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write24(struct ksz_device *dev, u32 reg, u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write24(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write32(dev, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_get(struct ksz_device *dev, u32 reg, void *data,
+                         size_t len)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->get(dev, reg, data, len);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int ksz_set(struct ksz_device *dev, u32 reg, void *data,
+                         size_t len)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->set(dev, reg, data, len);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline void ksz_pread8(struct ksz_device *dev, int port, int offset,
+                             u8 *data)
+{
+       ksz_read8(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pread16(struct ksz_device *dev, int port, int offset,
+                              u16 *data)
+{
+       ksz_read16(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pread32(struct ksz_device *dev, int port, int offset,
+                              u32 *data)
+{
+       ksz_read32(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset,
+                              u8 data)
+{
+       ksz_write8(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset,
+                               u16 data)
+{
+       ksz_write16(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset,
+                               u32 data)
+{
+       ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset), data);
+}
+
+static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
+{
+       u8 data;
+
+       ksz_read8(dev, addr, &data);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       ksz_write8(dev, addr, data);
+}
+
+static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
+                        bool set)
+{
+       u32 addr;
+       u8 data;
+
+       addr = dev->dev_ops->get_port_addr(port, offset);
+       ksz_read8(dev, addr, &data);
+
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+
+       ksz_write8(dev, addr, data);
+}
+
+#endif
index 2a98dbd..a38ff08 100644 (file)
@@ -1,19 +1,8 @@
-/*
- * Microchip KSZ series switch common definitions
- *
- * Copyright (C) 2017
+/* SPDX-License-Identifier: GPL-2.0
  *
- * 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.
+ * Microchip KSZ series switch common definitions
  *
- * 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.
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
  */
 
 #ifndef __KSZ_PRIV_H
@@ -25,7 +14,7 @@
 #include <linux/etherdevice.h>
 #include <net/dsa.h>
 
-#include "ksz_9477_reg.h"
+#include "ksz9477_reg.h"
 
 struct ksz_io_ops;
 
@@ -33,6 +22,27 @@ struct vlan_table {
        u32 table[3];
 };
 
+struct ksz_port_mib {
+       u8 cnt_ptr;
+       u64 *counters;
+};
+
+struct ksz_port {
+       u16 member;
+       u16 vid_member;
+       int stp_state;
+       struct phy_device phydev;
+
+       u32 on:1;                       /* port is not disabled by hardware */
+       u32 phy:1;                      /* port has a PHY */
+       u32 fiber:1;                    /* port is fiber */
+       u32 sgmii:1;                    /* port is SGMII */
+       u32 force:1;
+       u32 link_just_down:1;           /* link just goes down */
+
+       struct ksz_port_mib mib;
+};
+
 struct ksz_device {
        struct dsa_switch *ds;
        struct ksz_platform_data *pdata;
@@ -43,6 +53,7 @@ struct ksz_device {
        struct mutex alu_mutex;         /* ALU access */
        struct mutex vlan_mutex;        /* vlan access */
        const struct ksz_io_ops *ops;
+       const struct ksz_dev_ops *dev_ops;
 
        struct device *dev;
 
@@ -55,11 +66,37 @@ struct ksz_device {
        int num_statics;
        int cpu_port;                   /* port connected to CPU */
        int cpu_ports;                  /* port bitmap can be cpu port */
+       int phy_port_cnt;
        int port_cnt;
+       int reg_mib_cnt;
+       int mib_cnt;
+       int mib_port_cnt;
+       int last_port;                  /* ports after that not used */
+       phy_interface_t interface;
+       u32 regs_size;
 
        struct vlan_table *vlan_cache;
 
        u64 mib_value[TOTAL_SWITCH_COUNTER_NUM];
+
+       u8 *txbuf;
+
+       struct ksz_port *ports;
+       struct timer_list mib_read_timer;
+       struct work_struct mib_read;
+       unsigned long mib_read_interval;
+       u16 br_member;
+       u16 member;
+       u16 live_ports;
+       u16 on_ports;                   /* ports enabled by DSA */
+       u16 rx_ports;
+       u16 tx_ports;
+       u16 mirror_rx;
+       u16 mirror_tx;
+       u32 features;                   /* chip specific features */
+       u32 overrides;                  /* chip functions set by user */
+       u16 host_mask;
+       u16 port_mask;
 };
 
 struct ksz_io_ops {
@@ -71,140 +108,60 @@ struct ksz_io_ops {
        int (*write16)(struct ksz_device *dev, u32 reg, u16 value);
        int (*write24)(struct ksz_device *dev, u32 reg, u32 value);
        int (*write32)(struct ksz_device *dev, u32 reg, u32 value);
-       int (*phy_read16)(struct ksz_device *dev, int addr, int reg,
-                         u16 *value);
-       int (*phy_write16)(struct ksz_device *dev, int addr, int reg,
-                          u16 value);
+       int (*get)(struct ksz_device *dev, u32 reg, void *data, size_t len);
+       int (*set)(struct ksz_device *dev, u32 reg, void *data, size_t len);
+};
+
+struct alu_struct {
+       /* entry 1 */
+       u8      is_static:1;
+       u8      is_src_filter:1;
+       u8      is_dst_filter:1;
+       u8      prio_age:3;
+       u32     _reserv_0_1:23;
+       u8      mstp:3;
+       /* entry 2 */
+       u8      is_override:1;
+       u8      is_use_fid:1;
+       u32     _reserv_1_1:23;
+       u8      port_forward:7;
+       /* entry 3 & 4*/
+       u32     _reserv_2_1:9;
+       u8      fid:7;
+       u8      mac[ETH_ALEN];
+};
+
+struct ksz_dev_ops {
+       u32 (*get_port_addr)(int port, int offset);
+       void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member);
+       void (*flush_dyn_mac_table)(struct ksz_device *dev, int port);
+       void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port);
+       void (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val);
+       void (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val);
+       int (*r_dyn_mac_table)(struct ksz_device *dev, u16 addr, u8 *mac_addr,
+                              u8 *fid, u8 *src_port, u8 *timestamp,
+                              u16 *entries);
+       int (*r_sta_mac_table)(struct ksz_device *dev, u16 addr,
+                              struct alu_struct *alu);
+       void (*w_sta_mac_table)(struct ksz_device *dev, u16 addr,
+                               struct alu_struct *alu);
+       void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr,
+                         u64 *cnt);
+       void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr,
+                         u64 *dropped, u64 *cnt);
+       void (*port_init_cnt)(struct ksz_device *dev, int port);
+       int (*shutdown)(struct ksz_device *dev);
+       int (*detect)(struct ksz_device *dev);
+       int (*init)(struct ksz_device *dev);
+       void (*exit)(struct ksz_device *dev);
 };
 
 struct ksz_device *ksz_switch_alloc(struct device *base,
                                    const struct ksz_io_ops *ops, void *priv);
-int ksz_switch_detect(struct ksz_device *dev);
-int ksz_switch_register(struct ksz_device *dev);
+int ksz_switch_register(struct ksz_device *dev,
+                       const struct ksz_dev_ops *ops);
 void ksz_switch_remove(struct ksz_device *dev);
 
-static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read8(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read16(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read24(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read24(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read32(dev, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write8(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write16(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write24(struct ksz_device *dev, u32 reg, u32 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write24(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write32(dev, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline void ksz_pread8(struct ksz_device *dev, int port, int offset,
-                             u8 *data)
-{
-       ksz_read8(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pread16(struct ksz_device *dev, int port, int offset,
-                              u16 *data)
-{
-       ksz_read16(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pread32(struct ksz_device *dev, int port, int offset,
-                              u32 *data)
-{
-       ksz_read32(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset,
-                              u8 data)
-{
-       ksz_write8(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset,
-                               u16 data)
-{
-       ksz_write16(dev, PORT_CTRL_ADDR(port, offset), data);
-}
-
-static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset,
-                               u32 data)
-{
-       ksz_write32(dev, PORT_CTRL_ADDR(port, offset), data);
-}
+int ksz9477_switch_register(struct ksz_device *dev);
 
 #endif
diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c
deleted file mode 100644 (file)
index 8c1778b..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Microchip KSZ series register access through SPI
- *
- * Copyright (C) 2017
- *
- * 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 <asm/unaligned.h>
-
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spi/spi.h>
-
-#include "ksz_priv.h"
-
-/* SPI frame opcodes */
-#define KS_SPIOP_RD                    3
-#define KS_SPIOP_WR                    2
-
-#define SPI_ADDR_SHIFT                 24
-#define SPI_ADDR_MASK                  (BIT(SPI_ADDR_SHIFT) - 1)
-#define SPI_TURNAROUND_SHIFT           5
-
-static int ksz_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val,
-                           unsigned int len)
-{
-       u32 txbuf;
-       int ret;
-
-       txbuf = reg & SPI_ADDR_MASK;
-       txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT;
-       txbuf <<= SPI_TURNAROUND_SHIFT;
-       txbuf = cpu_to_be32(txbuf);
-
-       ret = spi_write_then_read(spi, &txbuf, 4, val, len);
-       return ret;
-}
-
-static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
-                       unsigned int len)
-{
-       struct spi_device *spi = dev->priv;
-
-       return ksz_spi_read_reg(spi, reg, data, len);
-}
-
-static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
-{
-       return ksz_spi_read(dev, reg, val, 1);
-}
-
-static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
-{
-       int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
-
-       if (!ret)
-               *val = be16_to_cpu(*val);
-
-       return ret;
-}
-
-static int ksz_spi_read24(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret;
-
-       *val = 0;
-       ret = ksz_spi_read(dev, reg, (u8 *)val, 3);
-       if (!ret) {
-               *val = be32_to_cpu(*val);
-               /* convert to 24bit */
-               *val >>= 8;
-       }
-
-       return ret;
-}
-
-static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
-{
-       int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
-
-       if (!ret)
-               *val = be32_to_cpu(*val);
-
-       return ret;
-}
-
-static int ksz_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val,
-                            unsigned int len)
-{
-       u32 txbuf;
-       u8 data[12];
-       int i;
-
-       txbuf = reg & SPI_ADDR_MASK;
-       txbuf |= (KS_SPIOP_WR << SPI_ADDR_SHIFT);
-       txbuf <<= SPI_TURNAROUND_SHIFT;
-       txbuf = cpu_to_be32(txbuf);
-
-       data[0] = txbuf & 0xFF;
-       data[1] = (txbuf & 0xFF00) >> 8;
-       data[2] = (txbuf & 0xFF0000) >> 16;
-       data[3] = (txbuf & 0xFF000000) >> 24;
-       for (i = 0; i < len; i++)
-               data[i + 4] = val[i];
-
-       return spi_write(spi, &data, 4 + len);
-}
-
-static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       return ksz_spi_write_reg(spi, reg, &value, 1);
-}
-
-static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       value = cpu_to_be16(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 2);
-}
-
-static int ksz_spi_write24(struct ksz_device *dev, u32 reg, u32 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       /* make it to big endian 24bit from MSB */
-       value <<= 8;
-       value = cpu_to_be32(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 3);
-}
-
-static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
-{
-       struct spi_device *spi = dev->priv;
-
-       value = cpu_to_be32(value);
-       return ksz_spi_write_reg(spi, reg, (u8 *)&value, 4);
-}
-
-static const struct ksz_io_ops ksz_spi_ops = {
-       .read8 = ksz_spi_read8,
-       .read16 = ksz_spi_read16,
-       .read24 = ksz_spi_read24,
-       .read32 = ksz_spi_read32,
-       .write8 = ksz_spi_write8,
-       .write16 = ksz_spi_write16,
-       .write24 = ksz_spi_write24,
-       .write32 = ksz_spi_write32,
-};
-
-static int ksz_spi_probe(struct spi_device *spi)
-{
-       struct ksz_device *dev;
-       int ret;
-
-       dev = ksz_switch_alloc(&spi->dev, &ksz_spi_ops, spi);
-       if (!dev)
-               return -ENOMEM;
-
-       if (spi->dev.platform_data)
-               dev->pdata = spi->dev.platform_data;
-
-       ret = ksz_switch_register(dev);
-       if (ret)
-               return ret;
-
-       spi_set_drvdata(spi, dev);
-
-       return 0;
-}
-
-static int ksz_spi_remove(struct spi_device *spi)
-{
-       struct ksz_device *dev = spi_get_drvdata(spi);
-
-       if (dev)
-               ksz_switch_remove(dev);
-
-       return 0;
-}
-
-static const struct of_device_id ksz_dt_ids[] = {
-       { .compatible = "microchip,ksz9477" },
-       { .compatible = "microchip,ksz9897" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, ksz_dt_ids);
-
-static struct spi_driver ksz_spi_driver = {
-       .driver = {
-               .name   = "ksz9477-switch",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(ksz_dt_ids),
-       },
-       .probe  = ksz_spi_probe,
-       .remove = ksz_spi_remove,
-};
-
-module_spi_driver(ksz_spi_driver);
-
-MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>");
-MODULE_DESCRIPTION("Microchip KSZ Series Switch SPI access Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz_spi.h b/drivers/net/dsa/microchip/ksz_spi.h
new file mode 100644 (file)
index 0000000..427811b
--- /dev/null
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Microchip KSZ series SPI access common header
+ *
+ * Copyright (C) 2017-2018 Microchip Technology Inc.
+ *     Tristram Ha <Tristram.Ha@microchip.com>
+ */
+
+#ifndef __KSZ_SPI_H
+#define __KSZ_SPI_H
+
+/* Chip dependent SPI access */
+static int ksz_spi_read(struct ksz_device *dev, u32 reg, u8 *data,
+                       unsigned int len);
+static int ksz_spi_write(struct ksz_device *dev, u32 reg, void *data,
+                        unsigned int len);
+
+static int ksz_spi_read8(struct ksz_device *dev, u32 reg, u8 *val)
+{
+       return ksz_spi_read(dev, reg, val, 1);
+}
+
+static int ksz_spi_read16(struct ksz_device *dev, u32 reg, u16 *val)
+{
+       int ret = ksz_spi_read(dev, reg, (u8 *)val, 2);
+
+       if (!ret)
+               *val = be16_to_cpu(*val);
+
+       return ret;
+}
+
+static int ksz_spi_read32(struct ksz_device *dev, u32 reg, u32 *val)
+{
+       int ret = ksz_spi_read(dev, reg, (u8 *)val, 4);
+
+       if (!ret)
+               *val = be32_to_cpu(*val);
+
+       return ret;
+}
+
+static int ksz_spi_write8(struct ksz_device *dev, u32 reg, u8 value)
+{
+       return ksz_spi_write(dev, reg, &value, 1);
+}
+
+static int ksz_spi_write16(struct ksz_device *dev, u32 reg, u16 value)
+{
+       value = cpu_to_be16(value);
+       return ksz_spi_write(dev, reg, &value, 2);
+}
+
+static int ksz_spi_write32(struct ksz_device *dev, u32 reg, u32 value)
+{
+       value = cpu_to_be32(value);
+       return ksz_spi_write(dev, reg, &value, 4);
+}
+
+static int ksz_spi_get(struct ksz_device *dev, u32 reg, void *data, size_t len)
+{
+       return ksz_spi_read(dev, reg, data, len);
+}
+
+static int ksz_spi_set(struct ksz_device *dev, u32 reg, void *data, size_t len)
+{
+       return ksz_spi_write(dev, reg, data, len);
+}
+
+#endif
index a5de9bf..74547f4 100644 (file)
@@ -658,7 +658,8 @@ static void mt7530_adjust_link(struct dsa_switch *ds, int port,
                        if (phydev->asym_pause)
                                rmt_adv |= LPA_PAUSE_ASYM;
 
-                       lcl_adv = ethtool_adv_to_lcl_adv_t(phydev->advertising);
+                       lcl_adv = linkmode_adv_to_lcl_adv_t(
+                               phydev->advertising);
                        flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
 
                        if (flowctrl & FLOW_CTRL_TX)
index e05d4ed..b603f8d 100644 (file)
@@ -2524,11 +2524,22 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
        mutex_unlock(&chip->reg_lock);
 
        if (reg == MII_PHYSID2) {
-               /* Some internal PHYS don't have a model number.  Use
-                * the mv88e6390 family model number instead.
-                */
-               if (!(val & 0x3f0))
-                       val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
+               /* Some internal PHYs don't have a model number. */
+               if (chip->info->family != MV88E6XXX_FAMILY_6165)
+                       /* Then there is the 6165 family. It gets is
+                        * PHYs correct. But it can also have two
+                        * SERDES interfaces in the PHY address
+                        * space. And these don't have a model
+                        * number. But they are not PHYs, so we don't
+                        * want to give them something a PHY driver
+                        * will recognise.
+                        *
+                        * Use the mv88e6390 family model number
+                        * instead, for anything which really could be
+                        * a PHY,
+                        */
+                       if (!(val & 0x3f0))
+                               val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
        }
 
        return err ? err : val;
@@ -3234,6 +3245,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3276,6 +3288,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390x_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3291,8 +3304,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390x_serdes_power,
-       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
-       .serdes_irq_free = mv88e6390_serdes_irq_free,
+       .serdes_irq_setup = mv88e6390x_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390x_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .phylink_validate = mv88e6390x_phylink_validate,
 };
@@ -3318,6 +3331,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3405,11 +3419,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
        .port_set_egress_floods = mv88e6352_port_set_egress_floods,
        .port_set_ether_type = mv88e6351_port_set_ether_type,
        .port_pause_limit = mv88e6390_port_pause_limit,
-       .port_set_cmode = mv88e6390x_port_set_cmode,
        .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3710,11 +3724,11 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
        .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
        .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
        .port_pause_limit = mv88e6390_port_pause_limit,
-       .port_set_cmode = mv88e6390x_port_set_cmode,
        .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3757,11 +3771,11 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
        .port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
        .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
        .port_pause_limit = mv88e6390_port_pause_limit,
-       .port_set_cmode = mv88e6390x_port_set_cmode,
        .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
        .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
        .port_link_state = mv88e6352_port_link_state,
        .port_get_cmode = mv88e6352_port_get_cmode,
+       .port_set_cmode = mv88e6390x_port_set_cmode,
        .stats_snapshot = mv88e6390_g1_stats_snapshot,
        .stats_set_histogram = mv88e6390_g1_stats_set_histogram,
        .stats_get_sset_count = mv88e6320_stats_get_sset_count,
@@ -3777,8 +3791,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390x_serdes_power,
-       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
-       .serdes_irq_free = mv88e6390_serdes_irq_free,
+       .serdes_irq_setup = mv88e6390x_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390x_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
index cd7db60..ebd26b6 100644 (file)
@@ -368,12 +368,15 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
        u16 reg;
        int err;
 
-       if (mode == PHY_INTERFACE_MODE_NA)
-               return 0;
-
        if (port != 9 && port != 10)
                return -EOPNOTSUPP;
 
+       /* Default to a slow mode, so freeing up SERDES interfaces for
+        * other ports which might use them for SFPs.
+        */
+       if (mode == PHY_INTERFACE_MODE_NA)
+               mode = PHY_INTERFACE_MODE_1000BASEX;
+
        switch (mode) {
        case PHY_INTERFACE_MODE_1000BASEX:
                cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X;
@@ -437,6 +440,21 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
        return 0;
 }
 
+int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+                            phy_interface_t mode)
+{
+       switch (mode) {
+       case PHY_INTERFACE_MODE_XGMII:
+       case PHY_INTERFACE_MODE_XAUI:
+       case PHY_INTERFACE_MODE_RXAUI:
+               return -EINVAL;
+       default:
+               break;
+       }
+
+       return mv88e6390x_port_set_cmode(chip, port, mode);
+}
+
 int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
 {
        int err;
index 36904c9..0d81866 100644 (file)
@@ -310,6 +310,8 @@ int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
                               u8 out);
 int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
                               u8 out);
+int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+                            phy_interface_t mode);
 int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
                              phy_interface_t mode);
 int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
index bb69650..2caa8c8 100644 (file)
@@ -619,15 +619,11 @@ out:
        return ret;
 }
 
-int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
+int mv88e6390x_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
 {
        int lane;
        int err;
 
-       /* Only support ports 9 and 10 at the moment */
-       if (port < 9)
-               return 0;
-
        lane = mv88e6390x_serdes_get_lane(chip, port);
 
        if (lane == -ENODEV)
@@ -663,11 +659,19 @@ int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
        return mv88e6390_serdes_irq_enable(chip, port, lane);
 }
 
-void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
+int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
+{
+       if (port < 9)
+               return 0;
+
+       return mv88e6390_serdes_irq_setup(chip, port);
+}
+
+void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
 {
        int lane = mv88e6390x_serdes_get_lane(chip, port);
 
-       if (port < 9)
+       if (lane == -ENODEV)
                return;
 
        if (lane < 0)
@@ -685,6 +689,14 @@ void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
        chip->ports[port].serdes_irq = 0;
 }
 
+void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
+{
+       if (port < 9)
+               return;
+
+       mv88e6390x_serdes_irq_free(chip, port);
+}
+
 int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
 {
        u8 cmode = chip->ports[port].cmode;
index 7870c5a..573dce8 100644 (file)
@@ -77,6 +77,8 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
 void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
+int mv88e6390x_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
+void mv88e6390x_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
                                 int port, uint8_t *data);
index 5bc1683..40f421d 100644 (file)
@@ -1151,7 +1151,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
 
        print_info = (vortex_debug > 1);
        if (print_info)
-               pr_info("See Documentation/networking/vortex.txt\n");
+               pr_info("See Documentation/networking/device_drivers/3com/vortex.txt\n");
 
        pr_info("%s: 3Com %s %s at %p.\n",
               print_name,
@@ -1956,7 +1956,7 @@ vortex_error(struct net_device *dev, int status)
                                   dev->name, tx_status);
                        if (tx_status == 0x82) {
                                pr_err("Probably a duplex mismatch.  See "
-                                               "Documentation/networking/vortex.txt\n");
+                                               "Documentation/networking/device_drivers/3com/vortex.txt\n");
                        }
                        dump_tx_ring(dev);
                }
index 5c3ef9f..0ac44ef 100644 (file)
@@ -75,8 +75,9 @@ config VORTEX
          "Hurricane" (3c555/3cSOHO)                           PCI
 
          If you have such a card, say Y here.  More specific information is in
-         <file:Documentation/networking/vortex.txt> and in the comments at
-         the beginning of <file:drivers/net/ethernet/3com/3c59x.c>.
+         <file:Documentation/networking/device_drivers/3com/vortex.txt> and
+         in the comments at the beginning of
+         <file:drivers/net/ethernet/3com/3c59x.c>.
 
          To compile this support as a module, choose M here.
 
index 7c9348a..91fc64c 100644 (file)
@@ -1283,7 +1283,7 @@ static int greth_mdio_probe(struct net_device *dev)
        else
                phy_set_max_speed(phy, SPEED_100);
 
-       phy->advertising = phy->supported;
+       linkmode_copy(phy->advertising, phy->supported);
 
        greth->link = 0;
        greth->speed = 0;
index 9d48998..bd6589d 100644 (file)
@@ -1488,9 +1488,9 @@ static int sunlance_sbus_probe(struct platform_device *op)
        struct device_node *parent_dp = parent->dev.of_node;
        int err;
 
-       if (!strcmp(parent_dp->name, "ledma")) {
+       if (of_node_name_eq(parent_dp, "ledma")) {
                err = sparc_lance_probe_one(op, parent, NULL);
-       } else if (!strcmp(parent_dp->name, "lebuffer")) {
+       } else if (of_node_name_eq(parent_dp, "lebuffer")) {
                err = sparc_lance_probe_one(op, NULL, parent);
        } else
                err = sparc_lance_probe_one(op, NULL, NULL);
index 151bdb6..128cd64 100644 (file)
@@ -857,6 +857,7 @@ static void xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata)
 
 static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
        struct xgbe_phy_data *phy_data = pdata->phy_data;
        unsigned int phy_id = phy_data->phydev->phy_id;
 
@@ -878,9 +879,15 @@ static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata)
        phy_write(phy_data->phydev, 0x04, 0x0d01);
        phy_write(phy_data->phydev, 0x00, 0x9140);
 
-       phy_data->phydev->supported = PHY_10BT_FEATURES |
-                                     PHY_100BT_FEATURES |
-                                     PHY_1000BT_FEATURES;
+       linkmode_set_bit_array(phy_10_100_features_array,
+                              ARRAY_SIZE(phy_10_100_features_array),
+                              supported);
+       linkmode_set_bit_array(phy_gbit_features_array,
+                              ARRAY_SIZE(phy_gbit_features_array),
+                              supported);
+
+       linkmode_copy(phy_data->phydev->supported, supported);
+
        phy_support_asym_pause(phy_data->phydev);
 
        netif_dbg(pdata, drv, pdata->netdev,
@@ -891,6 +898,7 @@ static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata)
 
 static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
        struct xgbe_phy_data *phy_data = pdata->phy_data;
        struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom;
        unsigned int phy_id = phy_data->phydev->phy_id;
@@ -951,9 +959,13 @@ static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata)
        reg = phy_read(phy_data->phydev, 0x00);
        phy_write(phy_data->phydev, 0x00, reg & ~0x00800);
 
-       phy_data->phydev->supported = (PHY_10BT_FEATURES |
-                                      PHY_100BT_FEATURES |
-                                      PHY_1000BT_FEATURES);
+       linkmode_set_bit_array(phy_10_100_features_array,
+                              ARRAY_SIZE(phy_10_100_features_array),
+                              supported);
+       linkmode_set_bit_array(phy_gbit_features_array,
+                              ARRAY_SIZE(phy_gbit_features_array),
+                              supported);
+       linkmode_copy(phy_data->phydev->supported, supported);
        phy_support_asym_pause(phy_data->phydev);
 
        netif_dbg(pdata, drv, pdata->netdev,
@@ -976,7 +988,6 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
        struct ethtool_link_ksettings *lks = &pdata->phy.lks;
        struct xgbe_phy_data *phy_data = pdata->phy_data;
        struct phy_device *phydev;
-       u32 advertising;
        int ret;
 
        /* If we already have a PHY, just return */
@@ -1036,9 +1047,8 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
 
        xgbe_phy_external_phy_quirks(pdata);
 
-       ethtool_convert_link_mode_to_legacy_u32(&advertising,
-                                               lks->link_modes.advertising);
-       phydev->advertising &= advertising;
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    lks->link_modes.advertising);
 
        phy_start_aneg(phy_data->phydev);
 
@@ -1497,7 +1507,7 @@ static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
        if (!phy_data->phydev)
                return;
 
-       lcl_adv = ethtool_adv_to_lcl_adv_t(phy_data->phydev->advertising);
+       lcl_adv = linkmode_adv_to_lcl_adv_t(phy_data->phydev->advertising);
 
        if (phy_data->phydev->pause) {
                XGBE_SET_LP_ADV(lks, Pause);
@@ -1815,7 +1825,6 @@ static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
 {
        struct ethtool_link_ksettings *lks = &pdata->phy.lks;
        struct xgbe_phy_data *phy_data = pdata->phy_data;
-       u32 advertising;
        int ret;
 
        ret = xgbe_phy_find_phy_device(pdata);
@@ -1825,12 +1834,10 @@ static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
        if (!phy_data->phydev)
                return 0;
 
-       ethtool_convert_link_mode_to_legacy_u32(&advertising,
-                                               lks->link_modes.advertising);
-
        phy_data->phydev->autoneg = pdata->phy.autoneg;
-       phy_data->phydev->advertising = phy_data->phydev->supported &
-                                       advertising;
+       linkmode_and(phy_data->phydev->advertising,
+                    phy_data->phydev->supported,
+                    lks->link_modes.advertising);
 
        if (pdata->phy.autoneg != AUTONEG_ENABLE) {
                phy_data->phydev->speed = pdata->phy.speed;
index f5fe3bb..53529cd 100644 (file)
@@ -109,6 +109,7 @@ void xge_mdio_remove(struct net_device *ndev)
 
 int xge_mdio_config(struct net_device *ndev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        struct xge_pdata *pdata = netdev_priv(ndev);
        struct device *dev = &pdata->pdev->dev;
        struct mii_bus *mdio_bus;
@@ -148,16 +149,17 @@ int xge_mdio_config(struct net_device *ndev)
                goto err;
        }
 
-       phydev->supported &= ~(SUPPORTED_10baseT_Half |
-                              SUPPORTED_10baseT_Full |
-                              SUPPORTED_100baseT_Half |
-                              SUPPORTED_100baseT_Full |
-                              SUPPORTED_1000baseT_Half |
-                              SUPPORTED_AUI |
-                              SUPPORTED_MII |
-                              SUPPORTED_FIBRE |
-                              SUPPORTED_BNC);
-       phydev->advertising = phydev->supported;
+       linkmode_set_bit_array(phy_10_100_features_array,
+                              ARRAY_SIZE(phy_10_100_features_array),
+                              mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_AUI_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_BNC_BIT, mask);
+
+       linkmode_andnot(phydev->supported, phydev->supported, mask);
+       linkmode_copy(phydev->advertising, phydev->supported);
        pdata->phy_speed = SPEED_UNKNOWN;
 
        return 0;
index 686f6d8..4556630 100644 (file)
@@ -36,6 +36,7 @@ atlantic-objs := aq_main.o \
        aq_ring.o \
        aq_hw_utils.o \
        aq_ethtool.o \
+       aq_filters.o \
        hw_atl/hw_atl_a0.o \
        hw_atl/hw_atl_b0.o \
        hw_atl/hw_atl_utils.o \
index 91eb891..3944ce7 100644 (file)
@@ -12,7 +12,7 @@
 #ifndef AQ_CFG_H
 #define AQ_CFG_H
 
-#define AQ_CFG_VECS_DEF   4U
+#define AQ_CFG_VECS_DEF   8U
 #define AQ_CFG_TCS_DEF    1U
 
 #define AQ_CFG_TXDS_DEF    4096U
@@ -42,8 +42,8 @@
 #define AQ_CFG_IS_LRO_DEF           1U
 
 /* RSS */
-#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX  128U
-#define AQ_CFG_RSS_HASHKEY_SIZE           320U
+#define AQ_CFG_RSS_INDIRECTION_TABLE_MAX  64U
+#define AQ_CFG_RSS_HASHKEY_SIZE           40U
 
 #define AQ_CFG_IS_RSS_DEF           1U
 #define AQ_CFG_NUM_RSS_QUEUES_DEF   AQ_CFG_VECS_DEF
index becb578..6b6d172 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/pci.h>
-
+#include <linux/if_vlan.h>
 #include "ver.h"
 #include "aq_cfg.h"
 #include "aq_utils.h"
index 99ef1da..38e87ee 100644 (file)
@@ -12,6 +12,7 @@
 #include "aq_ethtool.h"
 #include "aq_nic.h"
 #include "aq_vec.h"
+#include "aq_filters.h"
 
 static void aq_ethtool_get_regs(struct net_device *ndev,
                                struct ethtool_regs *regs, void *p)
@@ -201,6 +202,41 @@ static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key,
        return 0;
 }
 
+static int aq_ethtool_set_rss(struct net_device *netdev, const u32 *indir,
+                             const u8 *key, const u8 hfunc)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(netdev);
+       struct aq_nic_cfg_s *cfg;
+       unsigned int i = 0U;
+       u32 rss_entries;
+       int err = 0;
+
+       cfg = aq_nic_get_cfg(aq_nic);
+       rss_entries = cfg->aq_rss.indirection_table_size;
+
+       /* We do not allow change in unsupported parameters */
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+               return -EOPNOTSUPP;
+       /* Fill out the redirection table */
+       if (indir)
+               for (i = 0; i < rss_entries; i++)
+                       cfg->aq_rss.indirection_table[i] = indir[i];
+
+       /* Fill out the rss hash key */
+       if (key) {
+               memcpy(cfg->aq_rss.hash_secret_key, key,
+                      sizeof(cfg->aq_rss.hash_secret_key));
+               err = aq_nic->aq_hw_ops->hw_rss_hash_set(aq_nic->aq_hw,
+                       &cfg->aq_rss);
+               if (err)
+                       return err;
+       }
+
+       err = aq_nic->aq_hw_ops->hw_rss_set(aq_nic->aq_hw, &cfg->aq_rss);
+
+       return err;
+}
+
 static int aq_ethtool_get_rxnfc(struct net_device *ndev,
                                struct ethtool_rxnfc *cmd,
                                u32 *rule_locs)
@@ -213,7 +249,36 @@ static int aq_ethtool_get_rxnfc(struct net_device *ndev,
        case ETHTOOL_GRXRINGS:
                cmd->data = cfg->vecs;
                break;
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic);
+               break;
+       case ETHTOOL_GRXCLSRULE:
+               err = aq_get_rxnfc_rule(aq_nic, cmd);
+               break;
+       case ETHTOOL_GRXCLSRLALL:
+               err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static int aq_ethtool_set_rxnfc(struct net_device *ndev,
+                               struct ethtool_rxnfc *cmd)
+{
+       int err = 0;
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
 
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               err = aq_add_rxnfc_rule(aq_nic, cmd);
+               break;
+       case ETHTOOL_SRXCLSRLDEL:
+               err = aq_del_rxnfc_rule(aq_nic, cmd);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -495,7 +560,7 @@ static int aq_set_ringparam(struct net_device *ndev,
                }
        }
        if (ndev_running)
-               err = dev_open(ndev);
+               err = dev_open(ndev, NULL);
 
 err_exit:
        return err;
@@ -519,7 +584,9 @@ const struct ethtool_ops aq_ethtool_ops = {
        .set_pauseparam      = aq_ethtool_set_pauseparam,
        .get_rxfh_key_size   = aq_ethtool_get_rss_key_size,
        .get_rxfh            = aq_ethtool_get_rss,
+       .set_rxfh            = aq_ethtool_set_rss,
        .get_rxnfc           = aq_ethtool_get_rxnfc,
+       .set_rxnfc           = aq_ethtool_set_rxnfc,
        .get_sset_count      = aq_ethtool_get_sset_count,
        .get_ethtool_stats   = aq_ethtool_stats,
        .get_link_ksettings  = aq_ethtool_get_link_ksettings,
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c
new file mode 100644 (file)
index 0000000..18bc035
--- /dev/null
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2014-2017 aQuantia Corporation. */
+
+/* File aq_filters.c: RX filters related functions. */
+
+#include "aq_filters.h"
+
+static bool __must_check
+aq_rule_is_approve(struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->flow_type & FLOW_MAC_EXT)
+               return false;
+
+       switch (fsp->flow_type & ~FLOW_EXT) {
+       case ETHER_FLOW:
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW:
+       case SCTP_V4_FLOW:
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+       case SCTP_V6_FLOW:
+       case IPV4_FLOW:
+       case IPV6_FLOW:
+               return true;
+       case IP_USER_FLOW:
+               switch (fsp->h_u.usr_ip4_spec.proto) {
+               case IPPROTO_TCP:
+               case IPPROTO_UDP:
+               case IPPROTO_SCTP:
+               case IPPROTO_IP:
+                       return true;
+               default:
+                       return false;
+                       }
+       case IPV6_USER_FLOW:
+               switch (fsp->h_u.usr_ip6_spec.l4_proto) {
+               case IPPROTO_TCP:
+               case IPPROTO_UDP:
+               case IPPROTO_SCTP:
+               case IPPROTO_IP:
+                       return true;
+               default:
+                       return false;
+                       }
+       default:
+               return false;
+       }
+
+       return false;
+}
+
+static bool __must_check
+aq_match_filter(struct ethtool_rx_flow_spec *fsp1,
+               struct ethtool_rx_flow_spec *fsp2)
+{
+       if (fsp1->flow_type != fsp2->flow_type ||
+           memcmp(&fsp1->h_u, &fsp2->h_u, sizeof(fsp2->h_u)) ||
+           memcmp(&fsp1->h_ext, &fsp2->h_ext, sizeof(fsp2->h_ext)) ||
+           memcmp(&fsp1->m_u, &fsp2->m_u, sizeof(fsp2->m_u)) ||
+           memcmp(&fsp1->m_ext, &fsp2->m_ext, sizeof(fsp2->m_ext)))
+               return false;
+
+       return true;
+}
+
+static bool __must_check
+aq_rule_already_exists(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       struct aq_rx_filter *rule;
+       struct hlist_node *aq_node2;
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location == fsp->location)
+                       continue;
+               if (aq_match_filter(&rule->aq_fsp, fsp)) {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: This filter is already set\n");
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic,
+                                 struct aq_hw_rx_fltrs_s *rx_fltrs,
+                                 struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 ||
+           fsp->location > AQ_RX_LAST_LOC_FL3L4) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FL3L4,
+                          AQ_RX_LAST_LOC_FL3L4);
+               return -EINVAL;
+       }
+       if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) {
+               rx_fltrs->fl3l4.is_ipv6 = false;
+               netdev_err(aq_nic->ndev,
+                          "ethtool: mixing ipv4 and ipv6 is not allowed");
+               return -EINVAL;
+       } else if (!rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv6) {
+               rx_fltrs->fl3l4.is_ipv6 = true;
+               netdev_err(aq_nic->ndev,
+                          "ethtool: mixing ipv4 and ipv6 is not allowed");
+               return -EINVAL;
+       } else if (rx_fltrs->fl3l4.is_ipv6                    &&
+                  fsp->location != AQ_RX_FIRST_LOC_FL3L4 + 4 &&
+                  fsp->location != AQ_RX_FIRST_LOC_FL3L4) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified location for ipv6 must be %d or %d",
+                          AQ_RX_FIRST_LOC_FL3L4, AQ_RX_FIRST_LOC_FL3L4 + 4);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __must_check
+aq_check_approve_fl2(struct aq_nic_s *aq_nic,
+                    struct aq_hw_rx_fltrs_s *rx_fltrs,
+                    struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FETHERT ||
+           fsp->location > AQ_RX_LAST_LOC_FETHERT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FETHERT,
+                          AQ_RX_LAST_LOC_FETHERT);
+               return -EINVAL;
+       }
+
+       if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK &&
+           fsp->m_u.ether_spec.h_proto == 0U) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: proto (ether_type) parameter must be specified");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __must_check
+aq_check_approve_fvlan(struct aq_nic_s *aq_nic,
+                      struct aq_hw_rx_fltrs_s *rx_fltrs,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FVLANID ||
+           fsp->location > AQ_RX_LAST_LOC_FVLANID) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FVLANID,
+                          AQ_RX_LAST_LOC_FVLANID);
+               return -EINVAL;
+       }
+
+       if ((aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
+           (!test_bit(be16_to_cpu(fsp->h_ext.vlan_tci),
+                      aq_nic->active_vlans))) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: unknown vlan-id specified");
+               return -EINVAL;
+       }
+
+       if (fsp->ring_cookie > aq_nic->aq_nic_cfg.num_rss_queues) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: queue number must be in range [0, %d]",
+                          aq_nic->aq_nic_cfg.num_rss_queues - 1);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int __must_check
+aq_check_filter(struct aq_nic_s *aq_nic,
+               struct ethtool_rx_flow_spec *fsp)
+{
+       int err = 0;
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       if (fsp->flow_type & FLOW_EXT) {
+               if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_VID_MASK) {
+                       err = aq_check_approve_fvlan(aq_nic, rx_fltrs, fsp);
+               } else if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK) {
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
+               } else {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: invalid vlan mask 0x%x specified",
+                                  be16_to_cpu(fsp->m_ext.vlan_tci));
+                       err = -EINVAL;
+               }
+       } else {
+               switch (fsp->flow_type & ~FLOW_EXT) {
+               case ETHER_FLOW:
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
+                       break;
+               case TCP_V4_FLOW:
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case IPV4_FLOW:
+               case IP_USER_FLOW:
+                       rx_fltrs->fl3l4.is_ipv6 = false;
+                       err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
+                       break;
+               case TCP_V6_FLOW:
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case IPV6_FLOW:
+               case IPV6_USER_FLOW:
+                       rx_fltrs->fl3l4.is_ipv6 = true;
+                       err = aq_check_approve_fl3l4(aq_nic, rx_fltrs, fsp);
+                       break;
+               default:
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: unknown flow-type specified");
+                       err = -EINVAL;
+               }
+       }
+
+       return err;
+}
+
+static bool __must_check
+aq_rule_is_not_support(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       bool rule_is_not_support = false;
+
+       if (!(aq_nic->ndev->features & NETIF_F_NTUPLE)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: Please, to enable the RX flow control:\n"
+                          "ethtool -K %s ntuple on\n", aq_nic->ndev->name);
+               rule_is_not_support = true;
+       } else if (!aq_rule_is_approve(fsp)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified flow type is not supported\n");
+               rule_is_not_support = true;
+       } else if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW &&
+                  (fsp->h_u.tcp_ip4_spec.tos ||
+                   fsp->h_u.tcp_ip6_spec.tclass)) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified tos tclass are not supported\n");
+               rule_is_not_support = true;
+       } else if (fsp->flow_type & FLOW_MAC_EXT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: MAC_EXT is not supported");
+               rule_is_not_support = true;
+       }
+
+       return rule_is_not_support;
+}
+
+static bool __must_check
+aq_rule_is_not_correct(struct aq_nic_s *aq_nic,
+                      struct ethtool_rx_flow_spec *fsp)
+{
+       bool rule_is_not_correct = false;
+
+       if (!aq_nic) {
+               rule_is_not_correct = true;
+       } else if (fsp->location > AQ_RX_MAX_RXNFC_LOC) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: The specified number %u rule is invalid\n",
+                          fsp->location);
+               rule_is_not_correct = true;
+       } else if (aq_check_filter(aq_nic, fsp)) {
+               rule_is_not_correct = true;
+       } else if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
+               if (fsp->ring_cookie >= aq_nic->aq_nic_cfg.num_rss_queues) {
+                       netdev_err(aq_nic->ndev,
+                                  "ethtool: The specified action is invalid.\n"
+                                  "Maximum allowable value action is %u.\n",
+                                  aq_nic->aq_nic_cfg.num_rss_queues - 1);
+                       rule_is_not_correct = true;
+               }
+       }
+
+       return rule_is_not_correct;
+}
+
+static int __must_check
+aq_check_rule(struct aq_nic_s *aq_nic,
+             struct ethtool_rx_flow_spec *fsp)
+{
+       int err = 0;
+
+       if (aq_rule_is_not_correct(aq_nic, fsp))
+               err = -EINVAL;
+       else if (aq_rule_is_not_support(aq_nic, fsp))
+               err = -EOPNOTSUPP;
+       else if (aq_rule_already_exists(aq_nic, fsp))
+               err = -EEXIST;
+
+       return err;
+}
+
+static void aq_set_data_fl2(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr,
+                           struct aq_rx_filter_l2 *data, bool add)
+{
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+
+       memset(data, 0, sizeof(*data));
+
+       data->location = fsp->location - AQ_RX_FIRST_LOC_FETHERT;
+
+       if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
+               data->queue = fsp->ring_cookie;
+       else
+               data->queue = -1;
+
+       data->ethertype = be16_to_cpu(fsp->h_u.ether_spec.h_proto);
+       data->user_priority_en = be16_to_cpu(fsp->m_ext.vlan_tci)
+                                == VLAN_PRIO_MASK;
+       data->user_priority = (be16_to_cpu(fsp->h_ext.vlan_tci)
+                              & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+}
+
+static int aq_add_del_fether(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       struct aq_rx_filter_l2 data;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+
+       aq_set_data_fl2(aq_nic, aq_rx_fltr, &data, add);
+
+       if (unlikely(!aq_hw_ops->hw_filter_l2_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_l2_clear))
+               return -EOPNOTSUPP;
+
+       if (add)
+               return aq_hw_ops->hw_filter_l2_set(aq_hw, &data);
+       else
+               return aq_hw_ops->hw_filter_l2_clear(aq_hw, &data);
+}
+
+static bool aq_fvlan_is_busy(struct aq_rx_filter_vlan *aq_vlans, int vlan)
+{
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].enable &&
+                   aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED &&
+                   aq_vlans[i].vlan_id == vlan) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+/* Function rebuilds array of vlan filters so that filters with assigned
+ * queue have a precedence over just vlans on the interface.
+ */
+static void aq_fvlan_rebuild(struct aq_nic_s *aq_nic,
+                            unsigned long *active_vlans,
+                            struct aq_rx_filter_vlan *aq_vlans)
+{
+       bool vlan_busy = false;
+       int vlan = -1;
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].enable &&
+                   aq_vlans[i].queue != AQ_RX_QUEUE_NOT_ASSIGNED)
+                       continue;
+               do {
+                       vlan = find_next_bit(active_vlans,
+                                            VLAN_N_VID,
+                                            vlan + 1);
+                       if (vlan == VLAN_N_VID) {
+                               aq_vlans[i].enable = 0U;
+                               aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED;
+                               aq_vlans[i].vlan_id = 0;
+                               continue;
+                       }
+
+                       vlan_busy = aq_fvlan_is_busy(aq_vlans, vlan);
+                       if (!vlan_busy) {
+                               aq_vlans[i].enable = 1U;
+                               aq_vlans[i].queue = AQ_RX_QUEUE_NOT_ASSIGNED;
+                               aq_vlans[i].vlan_id = vlan;
+                       }
+               } while (vlan_busy && vlan != VLAN_N_VID);
+       }
+}
+
+static int aq_set_data_fvlan(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr,
+                            struct aq_rx_filter_vlan *aq_vlans, bool add)
+{
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+       int location = fsp->location - AQ_RX_FIRST_LOC_FVLANID;
+       int i;
+
+       memset(&aq_vlans[location], 0, sizeof(aq_vlans[location]));
+
+       if (!add)
+               return 0;
+
+       /* remove vlan if it was in table without queue assignment */
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; ++i) {
+               if (aq_vlans[i].vlan_id ==
+                  (be16_to_cpu(fsp->h_ext.vlan_tci) & VLAN_VID_MASK)) {
+                       aq_vlans[i].enable = false;
+               }
+       }
+
+       aq_vlans[location].location = location;
+       aq_vlans[location].vlan_id = be16_to_cpu(fsp->h_ext.vlan_tci)
+                                    & VLAN_VID_MASK;
+       aq_vlans[location].queue = fsp->ring_cookie & 0x1FU;
+       aq_vlans[location].enable = 1U;
+
+       return 0;
+}
+
+int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id)
+                       break;
+       }
+       if (rule && be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) {
+               struct ethtool_rxnfc cmd;
+
+               cmd.fs.location = rule->aq_fsp.location;
+               return aq_del_rxnfc_rule(aq_nic, &cmd);
+       }
+
+       return -ENOENT;
+}
+
+static int aq_add_del_fvlan(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+
+       aq_set_data_fvlan(aq_nic,
+                         aq_rx_fltr,
+                         aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans,
+                         add);
+
+       return aq_filters_vlans_update(aq_nic);
+}
+
+static int aq_set_data_fl3l4(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr,
+                            struct aq_rx_filter_l3l4 *data, bool add)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+
+       memset(data, 0, sizeof(*data));
+
+       data->is_ipv6 = rx_fltrs->fl3l4.is_ipv6;
+       data->location = HW_ATL_GET_REG_LOCATION_FL3L4(fsp->location);
+
+       if (!add) {
+               if (!data->is_ipv6)
+                       rx_fltrs->fl3l4.active_ipv4 &= ~BIT(data->location);
+               else
+                       rx_fltrs->fl3l4.active_ipv6 &=
+                               ~BIT((data->location) / 4);
+
+               return 0;
+       }
+
+       data->cmd |= HW_ATL_RX_ENABLE_FLTR_L3L4;
+
+       switch (fsp->flow_type) {
+       case TCP_V4_FLOW:
+       case TCP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       case UDP_V4_FLOW:
+       case UDP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_UDP;
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       case SCTP_V4_FLOW:
+       case SCTP_V6_FLOW:
+               data->cmd |= HW_ATL_RX_SCTP;
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_PROT_L4;
+               break;
+       default:
+               break;
+       }
+
+       if (!data->is_ipv6) {
+               data->ip_src[0] =
+                       ntohl(fsp->h_u.tcp_ip4_spec.ip4src);
+               data->ip_dst[0] =
+                       ntohl(fsp->h_u.tcp_ip4_spec.ip4dst);
+               rx_fltrs->fl3l4.active_ipv4 |= BIT(data->location);
+       } else {
+               int i;
+
+               rx_fltrs->fl3l4.active_ipv6 |= BIT((data->location) / 4);
+               for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
+                       data->ip_dst[i] =
+                               ntohl(fsp->h_u.tcp_ip6_spec.ip6dst[i]);
+                       data->ip_src[i] =
+                               ntohl(fsp->h_u.tcp_ip6_spec.ip6src[i]);
+               }
+               data->cmd |= HW_ATL_RX_ENABLE_L3_IPV6;
+       }
+       if (fsp->flow_type != IP_USER_FLOW &&
+           fsp->flow_type != IPV6_USER_FLOW) {
+               if (!data->is_ipv6) {
+                       data->p_dst =
+                               ntohs(fsp->h_u.tcp_ip4_spec.pdst);
+                       data->p_src =
+                               ntohs(fsp->h_u.tcp_ip4_spec.psrc);
+               } else {
+                       data->p_dst =
+                               ntohs(fsp->h_u.tcp_ip6_spec.pdst);
+                       data->p_src =
+                               ntohs(fsp->h_u.tcp_ip6_spec.psrc);
+               }
+       }
+       if (data->ip_src[0] && !data->is_ipv6)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3;
+       if (data->ip_dst[0] && !data->is_ipv6)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3;
+       if (data->p_dst)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4;
+       if (data->p_src)
+               data->cmd |= HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4;
+       if (fsp->ring_cookie != RX_CLS_FLOW_DISC) {
+               data->cmd |= HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT;
+               data->cmd |= fsp->ring_cookie << HW_ATL_RX_QUEUE_FL3L4_SHIFT;
+               data->cmd |= HW_ATL_RX_ENABLE_QUEUE_L3L4;
+       } else {
+               data->cmd |= HW_ATL_RX_DISCARD << HW_ATL_RX_ACTION_FL3F4_SHIFT;
+       }
+
+       return 0;
+}
+
+static int aq_set_fl3l4(struct aq_hw_s *aq_hw,
+                       const struct aq_hw_ops *aq_hw_ops,
+                       struct aq_rx_filter_l3l4 *data)
+{
+       if (unlikely(!aq_hw_ops->hw_filter_l3l4_set))
+               return -EOPNOTSUPP;
+
+       return aq_hw_ops->hw_filter_l3l4_set(aq_hw, data);
+}
+
+static int aq_add_del_fl3l4(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       struct aq_rx_filter_l3l4 data;
+
+       if (unlikely(aq_rx_fltr->aq_fsp.location < AQ_RX_FIRST_LOC_FL3L4 ||
+                    aq_rx_fltr->aq_fsp.location > AQ_RX_LAST_LOC_FL3L4  ||
+                    aq_set_data_fl3l4(aq_nic, aq_rx_fltr, &data, add)))
+               return -EINVAL;
+
+       return aq_set_fl3l4(aq_hw, aq_hw_ops, &data);
+}
+
+static int aq_add_del_rule(struct aq_nic_s *aq_nic,
+                          struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       int err = -EINVAL;
+
+       if (aq_rx_fltr->aq_fsp.flow_type & FLOW_EXT) {
+               if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci)
+                   == VLAN_VID_MASK) {
+                       aq_rx_fltr->type = aq_rx_filter_vlan;
+                       err = aq_add_del_fvlan(aq_nic, aq_rx_fltr, add);
+               } else if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci)
+                       == VLAN_PRIO_MASK) {
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
+               }
+       } else {
+               switch (aq_rx_fltr->aq_fsp.flow_type & ~FLOW_EXT) {
+               case ETHER_FLOW:
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
+                       break;
+               case TCP_V4_FLOW:
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case IP_USER_FLOW:
+               case TCP_V6_FLOW:
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case IPV6_USER_FLOW:
+                       aq_rx_fltr->type = aq_rx_filter_l3l4;
+                       err = aq_add_del_fl3l4(aq_nic, aq_rx_fltr, add);
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int aq_update_table_filters(struct aq_nic_s *aq_nic,
+                                  struct aq_rx_filter *aq_rx_fltr, u16 index,
+                                  struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL, *parent = NULL;
+       struct hlist_node *aq_node2;
+       int err = -EINVAL;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location >= index)
+                       break;
+               parent = rule;
+       }
+
+       if (rule && rule->aq_fsp.location == index) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+
+       if (unlikely(!aq_rx_fltr))
+               return err;
+
+       INIT_HLIST_NODE(&aq_rx_fltr->aq_node);
+
+       if (parent)
+               hlist_add_behind(&aq_rx_fltr->aq_node, &parent->aq_node);
+       else
+               hlist_add_head(&aq_rx_fltr->aq_node, &rx_fltrs->filter_list);
+
+       ++rx_fltrs->active_filters;
+
+       return 0;
+}
+
+u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+
+       return rx_fltrs->active_filters;
+}
+
+struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic)
+{
+       return &aq_nic->aq_hw_rx_fltrs;
+}
+
+int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct aq_rx_filter *aq_rx_fltr;
+       int err = 0;
+
+       err = aq_check_rule(aq_nic, fsp);
+       if (err)
+               goto err_exit;
+
+       aq_rx_fltr = kzalloc(sizeof(*aq_rx_fltr), GFP_KERNEL);
+       if (unlikely(!aq_rx_fltr)) {
+               err = -ENOMEM;
+               goto err_exit;
+       }
+
+       memcpy(&aq_rx_fltr->aq_fsp, fsp, sizeof(*fsp));
+
+       err = aq_update_table_filters(aq_nic, aq_rx_fltr, fsp->location, NULL);
+       if (unlikely(err))
+               goto err_free;
+
+       err = aq_add_del_rule(aq_nic, aq_rx_fltr, true);
+       if (unlikely(err)) {
+               hlist_del(&aq_rx_fltr->aq_node);
+               --rx_fltrs->active_filters;
+               goto err_free;
+       }
+
+       return 0;
+
+err_free:
+       kfree(aq_rx_fltr);
+err_exit:
+       return err;
+}
+
+int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+       int err = -EINVAL;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (rule->aq_fsp.location == cmd->fs.location)
+                       break;
+       }
+
+       if (rule && rule->aq_fsp.location == cmd->fs.location) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+       return err;
+}
+
+int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct ethtool_rx_flow_spec *fsp =
+                       (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct aq_rx_filter *rule = NULL;
+       struct hlist_node *aq_node2;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node)
+               if (fsp->location <= rule->aq_fsp.location)
+                       break;
+
+       if (unlikely(!rule || fsp->location != rule->aq_fsp.location))
+               return -EINVAL;
+
+       memcpy(fsp, &rule->aq_fsp, sizeof(*fsp));
+
+       return 0;
+}
+
+int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
+                          u32 *rule_locs)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int count = 0;
+
+       cmd->data = aq_get_rxnfc_count_all_rules(aq_nic);
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               if (unlikely(count == cmd->rule_cnt))
+                       return -EMSGSIZE;
+
+               rule_locs[count++] = rule->aq_fsp.location;
+       }
+
+       cmd->rule_cnt = count;
+
+       return 0;
+}
+
+int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int err = 0;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               err = aq_add_del_rule(aq_nic, rule, false);
+               if (err)
+                       goto err_exit;
+               hlist_del(&rule->aq_node);
+               kfree(rule);
+               --rx_fltrs->active_filters;
+       }
+
+err_exit:
+       return err;
+}
+
+int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic)
+{
+       struct aq_hw_rx_fltrs_s *rx_fltrs = aq_get_hw_rx_fltrs(aq_nic);
+       struct hlist_node *aq_node2;
+       struct aq_rx_filter *rule;
+       int err = 0;
+
+       hlist_for_each_entry_safe(rule, aq_node2,
+                                 &rx_fltrs->filter_list, aq_node) {
+               err = aq_add_del_rule(aq_nic, rule, true);
+               if (err)
+                       goto err_exit;
+       }
+
+err_exit:
+       return err;
+}
+
+int aq_filters_vlans_update(struct aq_nic_s *aq_nic)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       int hweight = 0;
+       int err = 0;
+       int i;
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl))
+               return -EOPNOTSUPP;
+
+       aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans,
+                        aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans);
+
+       if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               for (i = 0; i < BITS_TO_LONGS(VLAN_N_VID); i++)
+                       hweight += hweight_long(aq_nic->active_vlans[i]);
+
+               err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false);
+               if (err)
+                       return err;
+       }
+
+       err = aq_hw_ops->hw_filter_vlan_set(aq_hw,
+                                           aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans
+                                          );
+       if (err)
+               return err;
+
+       if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               if (hweight < AQ_VLAN_MAX_FILTERS)
+                       err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, true);
+               /* otherwise left in promiscue mode */
+       }
+
+       return err;
+}
+
+int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic)
+{
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       int err = 0;
+
+       memset(aq_nic->active_vlans, 0, sizeof(aq_nic->active_vlans));
+       aq_fvlan_rebuild(aq_nic, aq_nic->active_vlans,
+                        aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans);
+
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_vlan_ctrl))
+               return -EOPNOTSUPP;
+
+       err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, false);
+       if (err)
+               return err;
+       err = aq_hw_ops->hw_filter_vlan_set(aq_hw,
+                                           aq_nic->aq_hw_rx_fltrs.fl2.aq_vlans
+                                          );
+       return err;
+}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.h b/drivers/net/ethernet/aquantia/atlantic/aq_filters.h
new file mode 100644 (file)
index 0000000..c6a08c6
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2014-2017 aQuantia Corporation. */
+
+/* File aq_filters.h: RX filters related functions. */
+
+#ifndef AQ_FILTERS_H
+#define AQ_FILTERS_H
+
+#include "aq_nic.h"
+
+enum aq_rx_filter_type {
+       aq_rx_filter_ethertype,
+       aq_rx_filter_vlan,
+       aq_rx_filter_l3l4
+};
+
+struct aq_rx_filter {
+       struct hlist_node aq_node;
+       enum aq_rx_filter_type type;
+       struct ethtool_rx_flow_spec aq_fsp;
+};
+
+u16 aq_get_rxnfc_count_all_rules(struct aq_nic_s *aq_nic);
+struct aq_hw_rx_fltrs_s *aq_get_hw_rx_fltrs(struct aq_nic_s *aq_nic);
+int aq_add_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
+int aq_del_rxnfc_rule(struct aq_nic_s *aq_nic, const struct ethtool_rxnfc *cmd);
+int aq_get_rxnfc_rule(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd);
+int aq_get_rxnfc_all_rules(struct aq_nic_s *aq_nic, struct ethtool_rxnfc *cmd,
+                          u32 *rule_locs);
+int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id);
+int aq_clear_rxnfc_all_rules(struct aq_nic_s *aq_nic);
+int aq_reapply_rxnfc_all_rules(struct aq_nic_s *aq_nic);
+int aq_filters_vlans_update(struct aq_nic_s *aq_nic);
+int aq_filters_vlan_offload_off(struct aq_nic_s *aq_nic);
+
+#endif /* AQ_FILTERS_H */
index a1e70da..81aab73 100644 (file)
 #include "aq_rss.h"
 #include "hw_atl/hw_atl_utils.h"
 
+#define AQ_RX_FIRST_LOC_FVLANID     0U
+#define AQ_RX_LAST_LOC_FVLANID    15U
+#define AQ_RX_FIRST_LOC_FETHERT    16U
+#define AQ_RX_LAST_LOC_FETHERT    31U
+#define AQ_RX_FIRST_LOC_FL3L4     32U
+#define AQ_RX_LAST_LOC_FL3L4      39U
+#define AQ_RX_MAX_RXNFC_LOC       AQ_RX_LAST_LOC_FL3L4
+#define AQ_VLAN_MAX_FILTERS   \
+                       (AQ_RX_LAST_LOC_FVLANID - AQ_RX_FIRST_LOC_FVLANID + 1U)
+#define AQ_RX_QUEUE_NOT_ASSIGNED   0xFFU
+
 /* NIC H/W capabilities */
 struct aq_hw_caps_s {
        u64 hw_features;
@@ -130,6 +141,7 @@ struct aq_hw_s {
 struct aq_ring_s;
 struct aq_ring_param_s;
 struct sk_buff;
+struct aq_rx_filter_l3l4;
 
 struct aq_hw_ops {
 
@@ -183,6 +195,23 @@ struct aq_hw_ops {
        int (*hw_packet_filter_set)(struct aq_hw_s *self,
                                    unsigned int packet_filter);
 
+       int (*hw_filter_l3l4_set)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l3l4 *data);
+
+       int (*hw_filter_l3l4_clear)(struct aq_hw_s *self,
+                                   struct aq_rx_filter_l3l4 *data);
+
+       int (*hw_filter_l2_set)(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data);
+
+       int (*hw_filter_l2_clear)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data);
+
+       int (*hw_filter_vlan_set)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_vlan *aq_vlans);
+
+       int (*hw_filter_vlan_ctrl)(struct aq_hw_s *self, bool enable);
+
        int (*hw_multicast_list_set)(struct aq_hw_s *self,
                                     u8 ar_mac[AQ_HW_MULTICAST_ADDRESS_MAX]
                                     [ETH_ALEN],
index 7c07eef..2a11c1e 100644 (file)
@@ -13,6 +13,7 @@
 #include "aq_nic.h"
 #include "aq_pci_func.h"
 #include "aq_ethtool.h"
+#include "aq_filters.h"
 
 #include <linux/netdevice.h>
 #include <linux/module.h>
@@ -49,6 +50,11 @@ static int aq_ndev_open(struct net_device *ndev)
        err = aq_nic_init(aq_nic);
        if (err < 0)
                goto err_exit;
+
+       err = aq_reapply_rxnfc_all_rules(aq_nic);
+       if (err < 0)
+               goto err_exit;
+
        err = aq_nic_start(aq_nic);
        if (err < 0)
                goto err_exit;
@@ -101,6 +107,21 @@ static int aq_ndev_set_features(struct net_device *ndev,
        bool is_lro = false;
        int err = 0;
 
+       if (!(features & NETIF_F_NTUPLE)) {
+               if (aq_nic->ndev->features & NETIF_F_NTUPLE) {
+                       err = aq_clear_rxnfc_all_rules(aq_nic);
+                       if (unlikely(err))
+                               goto err_exit;
+               }
+       }
+       if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
+               if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+                       err = aq_filters_vlan_offload_off(aq_nic);
+                       if (unlikely(err))
+                               goto err_exit;
+               }
+       }
+
        aq_cfg->features = features;
 
        if (aq_cfg->aq_hw_caps->hw_features & NETIF_F_LRO) {
@@ -119,6 +140,7 @@ static int aq_ndev_set_features(struct net_device *ndev,
                err = aq_nic->aq_hw_ops->hw_set_offload(aq_nic->aq_hw,
                                                        aq_cfg);
 
+err_exit:
        return err;
 }
 
@@ -147,6 +169,35 @@ static void aq_ndev_set_multicast_settings(struct net_device *ndev)
        aq_nic_set_multicast_list(aq_nic, ndev);
 }
 
+static int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto,
+                                 u16 vid)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+
+       if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
+               return -EOPNOTSUPP;
+
+       set_bit(vid, aq_nic->active_vlans);
+
+       return aq_filters_vlans_update(aq_nic);
+}
+
+static int aq_ndo_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto,
+                                  u16 vid)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+
+       if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
+               return -EOPNOTSUPP;
+
+       clear_bit(vid, aq_nic->active_vlans);
+
+       if (-ENOENT == aq_del_fvlan_by_vlan(aq_nic, vid))
+               return aq_filters_vlans_update(aq_nic);
+
+       return 0;
+}
+
 static const struct net_device_ops aq_ndev_ops = {
        .ndo_open = aq_ndev_open,
        .ndo_stop = aq_ndev_close,
@@ -154,5 +205,7 @@ static const struct net_device_ops aq_ndev_ops = {
        .ndo_set_rx_mode = aq_ndev_set_multicast_settings,
        .ndo_change_mtu = aq_ndev_change_mtu,
        .ndo_set_mac_address = aq_ndev_set_mac_address,
-       .ndo_set_features = aq_ndev_set_features
+       .ndo_set_features = aq_ndev_set_features,
+       .ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid,
 };
index 7abdc09..0147c03 100644 (file)
@@ -44,7 +44,7 @@ static void aq_nic_rss_init(struct aq_nic_s *self, unsigned int num_rss_queues)
        struct aq_rss_parameters *rss_params = &cfg->aq_rss;
        int i = 0;
 
-       static u8 rss_key[40] = {
+       static u8 rss_key[AQ_CFG_RSS_HASHKEY_SIZE] = {
                0x1e, 0xad, 0x71, 0x87, 0x65, 0xfc, 0x26, 0x7d,
                0x0d, 0x45, 0x67, 0x74, 0xcd, 0x06, 0x1a, 0x18,
                0xb6, 0xc1, 0xf0, 0xc7, 0xbb, 0x18, 0xbe, 0xf8,
@@ -84,10 +84,6 @@ void aq_nic_cfg_start(struct aq_nic_s *self)
 
        cfg->is_lro = AQ_CFG_IS_LRO_DEF;
 
-       cfg->vlan_id = 0U;
-
-       aq_nic_rss_init(self, cfg->num_rss_queues);
-
        /*descriptors */
        cfg->rxds = min(cfg->aq_hw_caps->rxds_max, AQ_CFG_RXDS_DEF);
        cfg->txds = min(cfg->aq_hw_caps->txds_max, AQ_CFG_TXDS_DEF);
@@ -108,6 +104,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self)
 
        cfg->num_rss_queues = min(cfg->vecs, AQ_CFG_NUM_RSS_QUEUES_DEF);
 
+       aq_nic_rss_init(self, cfg->num_rss_queues);
+
        cfg->irq_type = aq_pci_func_get_irq_type(self);
 
        if ((cfg->irq_type == AQ_HW_IRQ_LEGACY) ||
index 44ec47a..8e34c1e 100644 (file)
@@ -35,7 +35,6 @@ struct aq_nic_cfg_s {
        u32 mtu;
        u32 flow_control;
        u32 link_speed_msk;
-       u32 vlan_id;
        u32 wol;
        u16 is_mc_list_enabled;
        u16 mc_list_count;
@@ -61,6 +60,23 @@ struct aq_nic_cfg_s {
 #define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \
        ((_TC_) * AQ_CFG_TCS_MAX + (_VEC_))
 
+struct aq_hw_rx_fl2 {
+       struct aq_rx_filter_vlan aq_vlans[AQ_VLAN_MAX_FILTERS];
+};
+
+struct aq_hw_rx_fl3l4 {
+       u8   active_ipv4;
+       u8   active_ipv6:2;
+       u8 is_ipv6;
+};
+
+struct aq_hw_rx_fltrs_s {
+       struct hlist_head     filter_list;
+       u16                   active_filters;
+       struct aq_hw_rx_fl2   fl2;
+       struct aq_hw_rx_fl3l4 fl3l4;
+};
+
 struct aq_nic_s {
        atomic_t flags;
        struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX];
@@ -81,10 +97,13 @@ struct aq_nic_s {
                u32 count;
                u8 ar[AQ_HW_MULTICAST_ADDRESS_MAX][ETH_ALEN];
        } mc_list;
+       /* Bitmask of currently assigned vlans from linux */
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
 
        struct pci_dev *pdev;
        unsigned int msix_entry_mask;
        u32 irqvecs;
+       struct aq_hw_rx_fltrs_s aq_hw_rx_fltrs;
 };
 
 static inline struct device *aq_nic_get_dev(struct aq_nic_s *self)
index 1d5d6b8..c8b44cd 100644 (file)
@@ -19,6 +19,7 @@
 #include "aq_pci_func.h"
 #include "hw_atl/hw_atl_a0.h"
 #include "hw_atl/hw_atl_b0.h"
+#include "aq_filters.h"
 
 static const struct pci_device_id aq_pci_tbl[] = {
        { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_0001), },
@@ -309,6 +310,7 @@ static void aq_pci_remove(struct pci_dev *pdev)
        struct aq_nic_s *self = pci_get_drvdata(pdev);
 
        if (self->ndev) {
+               aq_clear_rxnfc_all_rules(self);
                if (self->ndev->reg_state == NETREG_REGISTERED)
                        unregister_netdev(self->ndev);
                aq_nic_free_vectors(self);
index a7e853f..b58ca7c 100644 (file)
@@ -21,7 +21,7 @@
 
 #define DEFAULT_B0_BOARD_BASIC_CAPABILITIES \
        .is_64_dma = true,                \
-       .msix_irqs = 4U,                  \
+       .msix_irqs = 8U,                  \
        .irq_mask = ~0U,                  \
        .vecs = HW_ATL_B0_RSS_MAX,        \
        .tcs = HW_ATL_B0_TC_MAX,          \
@@ -41,7 +41,9 @@
                        NETIF_F_RXHASH |  \
                        NETIF_F_SG |      \
                        NETIF_F_TSO |     \
-                       NETIF_F_LRO,      \
+                       NETIF_F_LRO |     \
+                       NETIF_F_NTUPLE |  \
+                       NETIF_F_HW_VLAN_CTAG_FILTER, \
        .hw_priv_flags = IFF_UNICAST_FLT, \
        .flow_control = true,             \
        .mtu = HW_ATL_B0_MTU_JUMBO,       \
@@ -319,20 +321,11 @@ static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self)
        hw_atl_rpf_vlan_outer_etht_set(self, 0x88A8U);
        hw_atl_rpf_vlan_inner_etht_set(self, 0x8100U);
 
-       if (cfg->vlan_id) {
-               hw_atl_rpf_vlan_flr_act_set(self, 1U, 0U);
-               hw_atl_rpf_vlan_id_flr_set(self, 0U, 0U);
-               hw_atl_rpf_vlan_flr_en_set(self, 0U, 0U);
+       hw_atl_rpf_vlan_prom_mode_en_set(self, 1);
 
-               hw_atl_rpf_vlan_accept_untagged_packets_set(self, 1U);
-               hw_atl_rpf_vlan_untagged_act_set(self, 1U);
-
-               hw_atl_rpf_vlan_flr_act_set(self, 1U, 1U);
-               hw_atl_rpf_vlan_id_flr_set(self, cfg->vlan_id, 0U);
-               hw_atl_rpf_vlan_flr_en_set(self, 1U, 1U);
-       } else {
-               hw_atl_rpf_vlan_prom_mode_en_set(self, 1);
-       }
+       // Always accept untagged packets
+       hw_atl_rpf_vlan_accept_untagged_packets_set(self, 1U);
+       hw_atl_rpf_vlan_untagged_act_set(self, 1U);
 
        /* Rx Interrupts */
        hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
@@ -945,6 +938,142 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
+static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self,
+                                   struct aq_rx_filter_l3l4 *data)
+{
+       u8 location = data->location;
+
+       if (!data->is_ipv6) {
+               hw_atl_rpfl3l4_cmd_clear(self, location);
+               hw_atl_rpf_l4_spd_set(self, 0U, location);
+               hw_atl_rpf_l4_dpd_set(self, 0U, location);
+               hw_atl_rpfl3l4_ipv4_src_addr_clear(self, location);
+               hw_atl_rpfl3l4_ipv4_dest_addr_clear(self, location);
+       } else {
+               int i;
+
+               for (i = 0; i < HW_ATL_RX_CNT_REG_ADDR_IPV6; ++i) {
+                       hw_atl_rpfl3l4_cmd_clear(self, location + i);
+                       hw_atl_rpf_l4_spd_set(self, 0U, location + i);
+                       hw_atl_rpf_l4_dpd_set(self, 0U, location + i);
+               }
+               hw_atl_rpfl3l4_ipv6_src_addr_clear(self, location);
+               hw_atl_rpfl3l4_ipv6_dest_addr_clear(self, location);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l3l4 *data)
+{
+       u8 location = data->location;
+
+       hw_atl_b0_hw_fl3l4_clear(self, data);
+
+       if (data->cmd) {
+               if (!data->is_ipv6) {
+                       hw_atl_rpfl3l4_ipv4_dest_addr_set(self,
+                                                         location,
+                                                         data->ip_dst[0]);
+                       hw_atl_rpfl3l4_ipv4_src_addr_set(self,
+                                                        location,
+                                                        data->ip_src[0]);
+               } else {
+                       hw_atl_rpfl3l4_ipv6_dest_addr_set(self,
+                                                         location,
+                                                         data->ip_dst);
+                       hw_atl_rpfl3l4_ipv6_src_addr_set(self,
+                                                        location,
+                                                        data->ip_src);
+               }
+       }
+       hw_atl_rpf_l4_dpd_set(self, data->p_dst, location);
+       hw_atl_rpf_l4_spd_set(self, data->p_src, location);
+       hw_atl_rpfl3l4_cmd_set(self, location, data->cmd);
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl2_set(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 1U, data->location);
+       hw_atl_rpf_etht_flr_set(self, data->ethertype, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self,
+                                            !!data->user_priority_en,
+                                            data->location);
+       if (data->user_priority_en)
+               hw_atl_rpf_etht_user_priority_set(self,
+                                                 data->user_priority,
+                                                 data->location);
+
+       if (data->queue < 0) {
+               hw_atl_rpf_etht_flr_act_set(self, 0U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 0U, data->location);
+       } else {
+               hw_atl_rpf_etht_flr_act_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_set(self, data->queue, data->location);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl2_clear(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 0U, data->location);
+       hw_atl_rpf_etht_flr_set(self, 0U, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self, 0U, data->location);
+
+       return aq_hw_err_from_flags(self);
+}
+
+/**
+ * @brief Set VLAN filter table
+ * @details Configure VLAN filter table to accept (and assign the queue) traffic
+ *  for the particular vlan ids.
+ * Note: use this function under vlan promisc mode not to lost the traffic
+ *
+ * @param aq_hw_s
+ * @param aq_rx_filter_vlan VLAN filter configuration
+ * @return 0 - OK, <0 - error
+ */
+static int hw_atl_b0_hw_vlan_set(struct aq_hw_s *self,
+                                struct aq_rx_filter_vlan *aq_vlans)
+{
+       int i;
+
+       for (i = 0; i < AQ_VLAN_MAX_FILTERS; i++) {
+               hw_atl_rpf_vlan_flr_en_set(self, 0U, i);
+               hw_atl_rpf_vlan_rxq_en_flr_set(self, 0U, i);
+               if (aq_vlans[i].enable) {
+                       hw_atl_rpf_vlan_id_flr_set(self,
+                                                  aq_vlans[i].vlan_id,
+                                                  i);
+                       hw_atl_rpf_vlan_flr_act_set(self, 1U, i);
+                       hw_atl_rpf_vlan_flr_en_set(self, 1U, i);
+                       if (aq_vlans[i].queue != 0xFF) {
+                               hw_atl_rpf_vlan_rxq_flr_set(self,
+                                                           aq_vlans[i].queue,
+                                                           i);
+                               hw_atl_rpf_vlan_rxq_en_flr_set(self, 1U, i);
+                       }
+               }
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_vlan_ctrl(struct aq_hw_s *self, bool enable)
+{
+       /* set promisc in case of disabing the vland filter */
+       hw_atl_rpf_vlan_prom_mode_en_set(self, !!!enable);
+
+       return aq_hw_err_from_flags(self);
+}
+
 const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_set_mac_address   = hw_atl_b0_hw_mac_addr_set,
        .hw_init              = hw_atl_b0_hw_init,
@@ -969,6 +1098,11 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_ring_rx_init             = hw_atl_b0_hw_ring_rx_init,
        .hw_ring_tx_init             = hw_atl_b0_hw_ring_tx_init,
        .hw_packet_filter_set        = hw_atl_b0_hw_packet_filter_set,
+       .hw_filter_l2_set            = hw_atl_b0_hw_fl2_set,
+       .hw_filter_l2_clear          = hw_atl_b0_hw_fl2_clear,
+       .hw_filter_l3l4_set          = hw_atl_b0_hw_fl3l4_set,
+       .hw_filter_vlan_set          = hw_atl_b0_hw_vlan_set,
+       .hw_filter_vlan_ctrl         = hw_atl_b0_hw_vlan_ctrl,
        .hw_multicast_list_set       = hw_atl_b0_hw_multicast_list_set,
        .hw_interrupt_moderation_set = hw_atl_b0_hw_interrupt_moderation_set,
        .hw_rss_set                  = hw_atl_b0_hw_rss_set,
index 5502ec5..939f77e 100644 (file)
@@ -898,6 +898,24 @@ void hw_atl_rpf_vlan_id_flr_set(struct aq_hw_s *aq_hw, u32 vlan_id_flr,
                            vlan_id_flr);
 }
 
+void hw_atl_rpf_vlan_rxq_en_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq_en,
+                                   u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_VL_RXQ_EN_F_ADR(filter),
+                           HW_ATL_RPF_VL_RXQ_EN_F_MSK,
+                           HW_ATL_RPF_VL_RXQ_EN_F_SHIFT,
+                           vlan_rxq_en);
+}
+
+void hw_atl_rpf_vlan_rxq_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq,
+                                u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_VL_RXQ_F_ADR(filter),
+                           HW_ATL_RPF_VL_RXQ_F_MSK,
+                           HW_ATL_RPF_VL_RXQ_F_SHIFT,
+                           vlan_rxq);
+};
+
 void hw_atl_rpf_etht_flr_en_set(struct aq_hw_s *aq_hw, u32 etht_flr_en,
                                u32 filter)
 {
@@ -965,6 +983,20 @@ void hw_atl_rpf_etht_flr_set(struct aq_hw_s *aq_hw, u32 etht_flr, u32 filter)
                            HW_ATL_RPF_ET_VALF_SHIFT, etht_flr);
 }
 
+void hw_atl_rpf_l4_spd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_L4_SPD_ADR(filter),
+                           HW_ATL_RPF_L4_SPD_MSK,
+                           HW_ATL_RPF_L4_SPD_SHIFT, val);
+}
+
+void hw_atl_rpf_l4_dpd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPF_L4_DPD_ADR(filter),
+                           HW_ATL_RPF_L4_DPD_MSK,
+                           HW_ATL_RPF_L4_DPD_SHIFT, val);
+}
+
 /* RPO: rx packet offload */
 void hw_atl_rpo_ipv4header_crc_offload_en_set(struct aq_hw_s *aq_hw,
                                              u32 ipv4header_crc_offload_en)
@@ -1476,3 +1508,80 @@ void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr)
                            HW_ATL_MCP_UP_FORCE_INTERRUPT_SHIFT,
                            up_force_intr);
 }
+
+void hw_atl_rpfl3l4_ipv4_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_DSTA_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_ipv4_src_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_SRCA_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_cmd_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_REG_CTRL_ADR(location), 0U);
+}
+
+void hw_atl_rpfl3l4_ipv6_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_DSTA_ADR(location + i),
+                               0U);
+}
+
+void hw_atl_rpfl3l4_ipv6_src_addr_clear(struct aq_hw_s *aq_hw, u8 location)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_SRCA_ADR(location + i),
+                               0U);
+}
+
+void hw_atl_rpfl3l4_ipv4_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 ipv4_dest)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_DSTA_ADR(location),
+                       ipv4_dest);
+}
+
+void hw_atl_rpfl3l4_ipv4_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 ipv4_src)
+{
+       aq_hw_write_reg(aq_hw,
+                       HW_ATL_RPF_L3_SRCA_ADR(location),
+                       ipv4_src);
+}
+
+void hw_atl_rpfl3l4_cmd_set(struct aq_hw_s *aq_hw, u8 location, u32 cmd)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL_RPF_L3_REG_CTRL_ADR(location), cmd);
+}
+
+void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 *ipv6_src)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_SRCA_ADR(location + i),
+                               ipv6_src[i]);
+}
+
+void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 *ipv6_dest)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i)
+               aq_hw_write_reg(aq_hw,
+                               HW_ATL_RPF_L3_DSTA_ADR(location + i),
+                               ipv6_dest[i]);
+}
index 41f2399..03c570d 100644 (file)
@@ -441,6 +441,14 @@ void hw_atl_rpf_vlan_flr_act_set(struct aq_hw_s *aq_hw, u32 vlan_filter_act,
 void hw_atl_rpf_vlan_id_flr_set(struct aq_hw_s *aq_hw, u32 vlan_id_flr,
                                u32 filter);
 
+/* Set VLAN RX queue assignment enable */
+void hw_atl_rpf_vlan_rxq_en_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq_en,
+                                   u32 filter);
+
+/* Set VLAN RX queue */
+void hw_atl_rpf_vlan_rxq_flr_set(struct aq_hw_s *aq_hw, u32 vlan_rxq,
+                                u32 filter);
+
 /* set ethertype filter enable */
 void hw_atl_rpf_etht_flr_en_set(struct aq_hw_s *aq_hw, u32 etht_flr_en,
                                u32 filter);
@@ -475,6 +483,12 @@ void hw_atl_rpf_etht_flr_act_set(struct aq_hw_s *aq_hw, u32 etht_flr_act,
 /* set ethertype filter */
 void hw_atl_rpf_etht_flr_set(struct aq_hw_s *aq_hw, u32 etht_flr, u32 filter);
 
+/* set L4 source port */
+void hw_atl_rpf_l4_spd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter);
+
+/* set L4 destination port */
+void hw_atl_rpf_l4_dpd_set(struct aq_hw_s *aq_hw, u32 val, u32 filter);
+
 /* rpo */
 
 /* set ipv4 header checksum offload enable */
@@ -704,4 +718,38 @@ void hw_atl_pci_pci_reg_res_dis_set(struct aq_hw_s *aq_hw, u32 pci_reg_res_dis);
 /* set uP Force Interrupt */
 void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr);
 
+/* clear ipv4 filter destination address */
+void hw_atl_rpfl3l4_ipv4_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv4 filter source address */
+void hw_atl_rpfl3l4_ipv4_src_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear command for filter l3-l4 */
+void hw_atl_rpfl3l4_cmd_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv6 filter destination address */
+void hw_atl_rpfl3l4_ipv6_dest_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* clear ipv6 filter source address */
+void hw_atl_rpfl3l4_ipv6_src_addr_clear(struct aq_hw_s *aq_hw, u8 location);
+
+/* set ipv4 filter destination address */
+void hw_atl_rpfl3l4_ipv4_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 ipv4_dest);
+
+/* set ipv4 filter source address */
+void hw_atl_rpfl3l4_ipv4_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 ipv4_src);
+
+/* set command for filter l3-l4 */
+void hw_atl_rpfl3l4_cmd_set(struct aq_hw_s *aq_hw, u8 location, u32 cmd);
+
+/* set ipv6 filter source address */
+void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 *ipv6_src);
+
+/* set ipv6 filter destination address */
+void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location,
+                                      u32 *ipv6_dest);
+
 #endif /* HW_ATL_LLH_H */
index a715fa3..8470d92 100644 (file)
 /* Default value of bitfield vl_id{F}[B:0] */
 #define HW_ATL_RPF_VL_ID_F_DEFAULT 0x0
 
-/* RX et_en{F} Bitfield Definitions
- * Preprocessor definitions for the bitfield "et_en{F}".
+/* RX vl_rxq_en{F} Bitfield Definitions
+ * Preprocessor definitions for the bitfield "vl_rxq{F}".
  * Parameter: filter {F} | stride size 0x4 | range [0, 15]
- * PORT="pif_rpf_et_en_i[0]"
- */
-
-/* Register address for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_ADR(filter) (0x00005300 + (filter) * 0x4)
-/* Bitmask for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_MSK 0x80000000
-/* Inverted bitmask for bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_MSKN 0x7FFFFFFF
-/* Lower bit position of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_SHIFT 31
-/* Width of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_WIDTH 1
-/* Default value of bitfield et_en{F} */
-#define HW_ATL_RPF_ET_EN_F_DEFAULT 0x0
+ * PORT="pif_rpf_vl_rxq_en_i"
+ */
+
+/* Register address for bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_ADR(filter) (0x00005290 + (filter) * 0x4)
+/* Bitmask for bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_MSK 0x10000000
+/* Inverted bitmask for bitfield vl_rxq_en{F}[ */
+#define HW_ATL_RPF_VL_RXQ_EN_F_MSKN 0xEFFFFFFF
+/* Lower bit position of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_SHIFT 28
+/* Width of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_WIDTH 1
+/* Default value of bitfield vl_rxq_en{F} */
+#define HW_ATL_RPF_VL_RXQ_EN_F_DEFAULT 0x0
+
+/* RX vl_rxq{F}[4:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "vl_rxq{F}[4:0]".
+ * Parameter: filter {F} | stride size 0x4 | range [0, 15]
+ * PORT="pif_rpf_vl_rxq0_i[4:0]"
+ */
+
+/* Register address for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_ADR(filter) (0x00005290 + (filter) * 0x4)
+/* Bitmask for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_MSK 0x01F00000
+/* Inverted bitmask for bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_MSKN 0xFE0FFFFF
+/* Lower bit position of bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_SHIFT 20
+/* Width of bitfield vl_rxw{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_WIDTH 5
+/* Default value of bitfield vl_rxq{F}[4:0] */
+#define HW_ATL_RPF_VL_RXQ_F_DEFAULT 0x0
 
 /* rx et_en{f} bitfield definitions
  * preprocessor definitions for the bitfield "et_en{f}".
 /* default value of bitfield et_val{f}[f:0] */
 #define HW_ATL_RPF_ET_VALF_DEFAULT 0x0
 
+/* RX l4_sp{D}[F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l4_sp{D}[F:0]".
+ * Parameter: srcport {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l4_sp0_i[15:0]"
+ */
+
+/* Register address for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_ADR(srcport) (0x00005400u + (srcport) * 0x4)
+/* Bitmask for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_MSK 0x0000FFFFu
+/* Inverted bitmask for bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_MSKN 0xFFFF0000u
+/* Lower bit position of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_SHIFT 0
+/* Width of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_WIDTH 16
+/* Default value of bitfield l4_sp{D}[F:0] */
+#define HW_ATL_RPF_L4_SPD_DEFAULT 0x0
+
+/* RX l4_dp{D}[F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l4_dp{D}[F:0]".
+ * Parameter: destport {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l4_dp0_i[15:0]"
+ */
+
+/* Register address for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_ADR(destport) (0x00005420u + (destport) * 0x4)
+/* Bitmask for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_MSK 0x0000FFFFu
+/* Inverted bitmask for bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_MSKN 0xFFFF0000u
+/* Lower bit position of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_SHIFT 0
+/* Width of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_WIDTH 16
+/* Default value of bitfield l4_dp{D}[F:0] */
+#define HW_ATL_RPF_L4_DPD_DEFAULT 0x0
+
 /* rx ipv4_chk_en bitfield definitions
  * preprocessor definitions for the bitfield "ipv4_chk_en".
  * port="pif_rpo_ipv4_chk_en_i"
 /* default value of bitfield uP Force Interrupt */
 #define HW_ATL_MCP_UP_FORCE_INTERRUPT_DEFAULT 0x0
 
+#define HW_ATL_RX_CTRL_ADDR_BEGIN_FL3L4   0x00005380
+#define HW_ATL_RX_SRCA_ADDR_BEGIN_FL3L4   0x000053B0
+#define HW_ATL_RX_DESTA_ADDR_BEGIN_FL3L4  0x000053D0
+
+#define HW_ATL_RPF_L3_REG_CTRL_ADR(location) (0x00005380 + (location) * 0x4)
+
+/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_sa0_i[31:0]"
+ */
+
+/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */
+#define HW_ATL_RPF_L3_SRCA_ADR(location) (0x000053B0 + (location) * 0x4)
+/* Bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_SHIFT 0
+/* Width of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_WIDTH 32
+/* Default value of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0
+
+/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_da0_i[31:0]"
+ */
+
+ /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */
+#define HW_ATL_RPF_L3_DSTA_ADR(location) (0x000053B0 + (location) * 0x4)
+/* Bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_SHIFT 0
+/* Width of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_WIDTH 32
+/* Default value of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0
+
 #endif /* HW_ATL_LLH_INTERNAL_H */
index 7def1cb..9b74a31 100644 (file)
@@ -263,6 +263,8 @@ int hw_atl_utils_soft_reset(struct aq_hw_s *self)
                AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) &
                                HW_ATL_MPI_STATE_MSK) == MPI_DEINIT,
                               10, 1000U);
+               if (err)
+                       return err;
        }
 
        if (self->rbl_enabled)
@@ -454,8 +456,6 @@ int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
                               (fw.val =
                                aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR),
                                fw.tid), 1000U, 100U);
-               if (err < 0)
-                       goto err_exit;
 
                if (fw.len == 0xFFFFU) {
                        err = hw_atl_utils_fw_rpc_call(self, sw.len);
@@ -463,8 +463,6 @@ int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
                                goto err_exit;
                }
        } while (sw.tid != fw.tid || 0xFFFFU == fw.len);
-       if (err < 0)
-               goto err_exit;
 
        if (rpc) {
                if (fw.len) {
index 3613fca..48278e3 100644 (file)
@@ -240,6 +240,64 @@ struct __packed offload_info {
        u8 buf[0];
 };
 
+enum hw_atl_rx_action_with_traffic {
+       HW_ATL_RX_DISCARD,
+       HW_ATL_RX_HOST,
+};
+
+struct aq_rx_filter_vlan {
+       u8 enable;
+       u8 location;
+       u16 vlan_id;
+       u8 queue;
+};
+
+struct aq_rx_filter_l2 {
+       s8 queue;
+       u8 location;
+       u8 user_priority_en;
+       u8 user_priority;
+       u16 ethertype;
+};
+
+struct aq_rx_filter_l3l4 {
+       u32 cmd;
+       u8 location;
+       u32 ip_dst[4];
+       u32 ip_src[4];
+       u16 p_dst;
+       u16 p_src;
+       u8 is_ipv6;
+};
+
+enum hw_atl_rx_protocol_value_l3l4 {
+       HW_ATL_RX_TCP,
+       HW_ATL_RX_UDP,
+       HW_ATL_RX_SCTP,
+       HW_ATL_RX_ICMP
+};
+
+enum hw_atl_rx_ctrl_registers_l3l4 {
+       HW_ATL_RX_ENABLE_MNGMNT_QUEUE_L3L4 = BIT(22),
+       HW_ATL_RX_ENABLE_QUEUE_L3L4        = BIT(23),
+       HW_ATL_RX_ENABLE_ARP_FLTR_L3       = BIT(24),
+       HW_ATL_RX_ENABLE_CMP_PROT_L4       = BIT(25),
+       HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4  = BIT(26),
+       HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4   = BIT(27),
+       HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3  = BIT(28),
+       HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3   = BIT(29),
+       HW_ATL_RX_ENABLE_L3_IPV6           = BIT(30),
+       HW_ATL_RX_ENABLE_FLTR_L3L4         = BIT(31)
+};
+
+#define HW_ATL_RX_QUEUE_FL3L4_SHIFT       8U
+#define HW_ATL_RX_ACTION_FL3F4_SHIFT      16U
+
+#define HW_ATL_RX_CNT_REG_ADDR_IPV6       4U
+
+#define HW_ATL_GET_REG_LOCATION_FL3L4(location) \
+       ((location) - AQ_RX_FIRST_LOC_FL3L4)
+
 #define HAL_ATLANTIC_UTILS_CHIP_MIPS         0x00000001U
 #define HAL_ATLANTIC_UTILS_CHIP_TPO2         0x00000002U
 #define HAL_ATLANTIC_UTILS_CHIP_RPF2         0x00000004U
index bd277b0..4406325 100644 (file)
@@ -432,7 +432,8 @@ static int arc_emac_open(struct net_device *ndev)
        phy_dev->autoneg = AUTONEG_ENABLE;
        phy_dev->speed = 0;
        phy_dev->duplex = 0;
-       phy_dev->advertising &= phy_dev->supported;
+       linkmode_and(phy_dev->advertising, phy_dev->advertising,
+                    phy_dev->supported);
 
        priv->last_rx_bd = 0;
 
index e445ab7..f448089 100644 (file)
@@ -2248,6 +2248,7 @@ static void b44_adjust_link(struct net_device *dev)
 
 static int b44_register_phy_one(struct b44 *bp)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        struct mii_bus *mii_bus;
        struct ssb_device *sdev = bp->sdev;
        struct phy_device *phydev;
@@ -2303,11 +2304,12 @@ static int b44_register_phy_one(struct b44 *bp)
        }
 
        /* mask with MAC supported features */
-       phydev->supported &= (SUPPORTED_100baseT_Half |
-                             SUPPORTED_100baseT_Full |
-                             SUPPORTED_Autoneg |
-                             SUPPORTED_MII);
-       phydev->advertising = phydev->supported;
+       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
+       linkmode_and(phydev->supported, phydev->supported, mask);
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        bp->old_link = 0;
        bp->phy_addr = phydev->mdio.addr;
index 0e2d99c..4574275 100644 (file)
@@ -1068,6 +1068,7 @@ static void mpd_enable_set(struct bcm_sysport_priv *priv, bool enable)
 
 static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
 {
+       unsigned int index;
        u32 reg;
 
        /* Disable RXCHK, active filters and Broadcom tag matching */
@@ -1076,6 +1077,15 @@ static void bcm_sysport_resume_from_wol(struct bcm_sysport_priv *priv)
                 RXCHK_BRCM_TAG_MATCH_SHIFT | RXCHK_EN | RXCHK_BRCM_TAG_EN);
        rxchk_writel(priv, reg, RXCHK_CONTROL);
 
+       /* Make sure we restore correct CID index in case HW lost
+        * its context during deep idle state
+        */
+       for_each_set_bit(index, priv->filters, RXCHK_BRCM_TAG_MAX) {
+               rxchk_writel(priv, priv->filters_loc[index] <<
+                            RXCHK_BRCM_TAG_CID_SHIFT, RXCHK_BRCM_TAG(index));
+               rxchk_writel(priv, 0xff00ffff, RXCHK_BRCM_TAG_MASK(index));
+       }
+
        /* Clear the MagicPacket detection logic */
        mpd_enable_set(priv, false);
 
@@ -2189,6 +2199,7 @@ static int bcm_sysport_rule_set(struct bcm_sysport_priv *priv,
        rxchk_writel(priv, reg, RXCHK_BRCM_TAG(index));
        rxchk_writel(priv, 0xff00ffff, RXCHK_BRCM_TAG_MASK(index));
 
+       priv->filters_loc[index] = nfc->fs.location;
        set_bit(index, priv->filters);
 
        return 0;
@@ -2208,6 +2219,7 @@ static int bcm_sysport_rule_del(struct bcm_sysport_priv *priv,
         * be taken care of during suspend time by bcm_sysport_suspend_to_wol
         */
        clear_bit(index, priv->filters);
+       priv->filters_loc[index] = 0;
 
        return 0;
 }
@@ -2312,7 +2324,7 @@ static int bcm_sysport_map_queues(struct notifier_block *nb,
        struct bcm_sysport_priv *priv;
        struct net_device *slave_dev;
        unsigned int num_tx_queues;
-       unsigned int q, start, port;
+       unsigned int q, qp, port;
        struct net_device *dev;
 
        priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier);
@@ -2351,20 +2363,61 @@ static int bcm_sysport_map_queues(struct notifier_block *nb,
 
        priv->per_port_num_tx_queues = num_tx_queues;
 
-       start = find_first_zero_bit(&priv->queue_bitmap, dev->num_tx_queues);
-       for (q = 0; q < num_tx_queues; q++) {
-               ring = &priv->tx_rings[q + start];
+       for (q = 0, qp = 0; q < dev->num_tx_queues && qp < num_tx_queues;
+            q++) {
+               ring = &priv->tx_rings[q];
+
+               if (ring->inspect)
+                       continue;
 
                /* Just remember the mapping actual programming done
                 * during bcm_sysport_init_tx_ring
                 */
-               ring->switch_queue = q;
+               ring->switch_queue = qp;
                ring->switch_port = port;
                ring->inspect = true;
                priv->ring_map[q + port * num_tx_queues] = ring;
+               qp++;
+       }
+
+       return 0;
+}
+
+static int bcm_sysport_unmap_queues(struct notifier_block *nb,
+                                   struct dsa_notifier_register_info *info)
+{
+       struct bcm_sysport_tx_ring *ring;
+       struct bcm_sysport_priv *priv;
+       struct net_device *slave_dev;
+       unsigned int num_tx_queues;
+       struct net_device *dev;
+       unsigned int q, port;
+
+       priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier);
+       if (priv->netdev != info->master)
+               return 0;
+
+       dev = info->master;
+
+       if (dev->netdev_ops != &bcm_sysport_netdev_ops)
+               return 0;
+
+       port = info->port_number;
+       slave_dev = info->info.dev;
+
+       num_tx_queues = slave_dev->real_num_tx_queues;
+
+       for (q = 0; q < dev->num_tx_queues; q++) {
+               ring = &priv->tx_rings[q];
 
-               /* Set all queues as being used now */
-               set_bit(q + start, &priv->queue_bitmap);
+               if (ring->switch_port != port)
+                       continue;
+
+               if (!ring->inspect)
+                       continue;
+
+               ring->inspect = false;
+               priv->ring_map[q + port * num_tx_queues] = NULL;
        }
 
        return 0;
@@ -2373,14 +2426,18 @@ static int bcm_sysport_map_queues(struct notifier_block *nb,
 static int bcm_sysport_dsa_notifier(struct notifier_block *nb,
                                    unsigned long event, void *ptr)
 {
-       struct dsa_notifier_register_info *info;
-
-       if (event != DSA_PORT_REGISTER)
-               return NOTIFY_DONE;
+       int ret = NOTIFY_DONE;
 
-       info = ptr;
+       switch (event) {
+       case DSA_PORT_REGISTER:
+               ret = bcm_sysport_map_queues(nb, ptr);
+               break;
+       case DSA_PORT_UNREGISTER:
+               ret = bcm_sysport_unmap_queues(nb, ptr);
+               break;
+       }
 
-       return notifier_from_errno(bcm_sysport_map_queues(nb, info));
+       return notifier_from_errno(ret);
 }
 
 #define REV_FMT        "v%2x.%02x"
index a7a2308..0887e63 100644 (file)
@@ -786,6 +786,7 @@ struct bcm_sysport_priv {
        /* Ethtool */
        u32                     msg_enable;
        DECLARE_BITMAP(filters, RXCHK_BRCM_TAG_MAX);
+       u32                     filters_loc[RXCHK_BRCM_TAG_MAX];
 
        struct bcm_sysport_stats64      stats64;
 
@@ -795,7 +796,6 @@ struct bcm_sysport_priv {
        /* map information between switch port queues and local queues */
        struct notifier_block   dsa_notifier;
        unsigned int            per_port_num_tx_queues;
-       unsigned long           queue_bitmap;
        struct bcm_sysport_tx_ring *ring_map[DSA_MAX_PORTS * 8];
 
 };
index a4a90b6..749d0ef 100644 (file)
@@ -1105,11 +1105,39 @@ static void bnx2x_get_drvinfo(struct net_device *dev,
                              struct ethtool_drvinfo *info)
 {
        struct bnx2x *bp = netdev_priv(dev);
+       char version[ETHTOOL_FWVERS_LEN];
+       int ext_dev_info_offset;
+       u32 mbi;
 
        strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
 
-       bnx2x_fill_fw_str(bp, info->fw_version, sizeof(info->fw_version));
+       memset(version, 0, sizeof(version));
+       snprintf(version, ETHTOOL_FWVERS_LEN, " storm %d.%d.%d.%d",
+                BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION,
+                BCM_5710_FW_REVISION_VERSION, BCM_5710_FW_ENGINEERING_VERSION);
+       strlcat(info->version, version, sizeof(info->version));
+
+       if (SHMEM2_HAS(bp, extended_dev_info_shared_addr)) {
+               ext_dev_info_offset = SHMEM2_RD(bp,
+                                               extended_dev_info_shared_addr);
+               mbi = REG_RD(bp, ext_dev_info_offset +
+                            offsetof(struct extended_dev_info_shared_cfg,
+                                     mbi_version));
+               if (mbi) {
+                       memset(version, 0, sizeof(version));
+                       snprintf(version, ETHTOOL_FWVERS_LEN, "mbi %d.%d.%d ",
+                                (mbi & 0xff000000) >> 24,
+                                (mbi & 0x00ff0000) >> 16,
+                                (mbi & 0x0000ff00) >> 8);
+                       strlcpy(info->fw_version, version,
+                               sizeof(info->fw_version));
+               }
+       }
+
+       memset(version, 0, sizeof(version));
+       bnx2x_fill_fw_str(bp, version, ETHTOOL_FWVERS_LEN);
+       strlcat(info->fw_version, version, sizeof(info->fw_version));
 
        strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info));
 }
index f8b8103..d9057c8 100644 (file)
@@ -1140,6 +1140,11 @@ struct shm_dev_info {                            /* size */
 
 };
 
+struct extended_dev_info_shared_cfg {
+       u32 reserved[18];
+       u32 mbi_version;
+       u32 mbi_date;
+};
 
 #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
        #error "Missing either LITTLE_ENDIAN or BIG_ENDIAN definition."
index d83233a..510dfc1 100644 (file)
@@ -5731,7 +5731,7 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
                if (realdev) {
                        dev = cnic_from_netdev(realdev);
                        if (dev) {
-                               vid |= VLAN_TAG_PRESENT;
+                               vid |= VLAN_CFI_MASK;   /* make non-zero */
                                cnic_rcv_netevent(dev->cnic_priv, event, vid);
                                cnic_put(dev);
                        }
index 2d6f090..983245c 100644 (file)
@@ -1169,7 +1169,7 @@ static int bcmgenet_power_down(struct bcmgenet_priv *priv,
                break;
        }
 
-       return 0;
+       return ret;
 }
 
 static void bcmgenet_power_up(struct bcmgenet_priv *priv,
@@ -3612,36 +3612,6 @@ static int bcmgenet_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int bcmgenet_suspend(struct device *d)
-{
-       struct net_device *dev = dev_get_drvdata(d);
-       struct bcmgenet_priv *priv = netdev_priv(dev);
-       int ret = 0;
-
-       if (!netif_running(dev))
-               return 0;
-
-       netif_device_detach(dev);
-
-       bcmgenet_netif_stop(dev);
-
-       if (!device_may_wakeup(d))
-               phy_suspend(dev->phydev);
-
-       /* Prepare the device for Wake-on-LAN and switch to the slow clock */
-       if (device_may_wakeup(d) && priv->wolopts) {
-               ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
-               clk_prepare_enable(priv->clk_wol);
-       } else if (priv->internal_phy) {
-               ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
-       }
-
-       /* Turn off the clocks */
-       clk_disable_unprepare(priv->clk);
-
-       return ret;
-}
-
 static int bcmgenet_resume(struct device *d)
 {
        struct net_device *dev = dev_get_drvdata(d);
@@ -3719,6 +3689,39 @@ out_clk_disable:
        clk_disable_unprepare(priv->clk);
        return ret;
 }
+
+static int bcmgenet_suspend(struct device *d)
+{
+       struct net_device *dev = dev_get_drvdata(d);
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       int ret = 0;
+
+       if (!netif_running(dev))
+               return 0;
+
+       netif_device_detach(dev);
+
+       bcmgenet_netif_stop(dev);
+
+       if (!device_may_wakeup(d))
+               phy_suspend(dev->phydev);
+
+       /* Prepare the device for Wake-on-LAN and switch to the slow clock */
+       if (device_may_wakeup(d) && priv->wolopts) {
+               ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
+               clk_prepare_enable(priv->clk_wol);
+       } else if (priv->internal_phy) {
+               ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
+       }
+
+       /* Turn off the clocks */
+       clk_disable_unprepare(priv->clk);
+
+       if (ret)
+               bcmgenet_resume(d);
+
+       return ret;
+}
 #endif /* CONFIG_PM_SLEEP */
 
 static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume);
index 2fbd027..57582ef 100644 (file)
@@ -186,6 +186,8 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
        }
 
        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
+       if (!(reg & MPD_EN))
+               return; /* already powered up so skip the rest */
        reg &= ~MPD_EN;
        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 
index a6cbaca..aceb9b7 100644 (file)
@@ -226,7 +226,8 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
                 * capabilities, use that knowledge to also configure the
                 * Reverse MII interface correctly.
                 */
-               if (dev->phydev->supported & PHY_1000BT_FEATURES)
+               if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                     dev->phydev->supported))
                        port_ctrl = PORT_MODE_EXT_RVMII_50;
                else
                        port_ctrl = PORT_MODE_EXT_RVMII_25;
@@ -317,7 +318,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
                return ret;
        }
 
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        /* The internal PHY has its link interrupts routed to the
         * Ethernet MAC ISRs. On GENETv5 there is a hardware issue
index 432c3b8..3b1397a 100644 (file)
 #include <uapi/linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 
-#ifdef CONFIG_SPARC
-#include <asm/idprom.h>
-#include <asm/prom.h>
-#endif
-
 #define BAR_0  0
 #define BAR_2  2
 
@@ -2157,7 +2152,8 @@ static void tg3_phy_start(struct tg3 *tp)
                phydev->speed = tp->link_config.speed;
                phydev->duplex = tp->link_config.duplex;
                phydev->autoneg = tp->link_config.autoneg;
-               phydev->advertising = tp->link_config.advertising;
+               ethtool_convert_legacy_u32_to_link_mode(
+                       phydev->advertising, tp->link_config.advertising);
        }
 
        phy_start(phydev);
@@ -4057,8 +4053,9 @@ static int tg3_power_down_prepare(struct tg3 *tp)
                do_low_power = false;
                if ((tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) &&
                    !(tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER)) {
+                       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising) = { 0, };
                        struct phy_device *phydev;
-                       u32 phyid, advertising;
+                       u32 phyid;
 
                        phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
 
@@ -4067,25 +4064,33 @@ static int tg3_power_down_prepare(struct tg3 *tp)
                        tp->link_config.speed = phydev->speed;
                        tp->link_config.duplex = phydev->duplex;
                        tp->link_config.autoneg = phydev->autoneg;
-                       tp->link_config.advertising = phydev->advertising;
-
-                       advertising = ADVERTISED_TP |
-                                     ADVERTISED_Pause |
-                                     ADVERTISED_Autoneg |
-                                     ADVERTISED_10baseT_Half;
+                       ethtool_convert_link_mode_to_legacy_u32(
+                               &tp->link_config.advertising,
+                               phydev->advertising);
+
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, advertising);
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                        advertising);
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                        advertising);
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                                        advertising);
 
                        if (tg3_flag(tp, ENABLE_ASF) || device_should_wake) {
-                               if (tg3_flag(tp, WOL_SPEED_100MB))
-                                       advertising |=
-                                               ADVERTISED_100baseT_Half |
-                                               ADVERTISED_100baseT_Full |
-                                               ADVERTISED_10baseT_Full;
-                               else
-                                       advertising |= ADVERTISED_10baseT_Full;
+                               if (tg3_flag(tp, WOL_SPEED_100MB)) {
+                                       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                                        advertising);
+                                       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                                        advertising);
+                                       linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                                                        advertising);
+                               } else {
+                                       linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                                                        advertising);
+                               }
                        }
 
-                       phydev->advertising = advertising;
-
+                       linkmode_copy(phydev->advertising, advertising);
                        phy_start_aneg(phydev);
 
                        phyid = phydev->drv->phy_id & phydev->drv->phy_id_mask;
@@ -6135,10 +6140,16 @@ static int tg3_setup_phy(struct tg3 *tp, bool force_reset)
 }
 
 /* tp->lock must be held */
-static u64 tg3_refclk_read(struct tg3 *tp)
+static u64 tg3_refclk_read(struct tg3 *tp, struct ptp_system_timestamp *sts)
 {
-       u64 stamp = tr32(TG3_EAV_REF_CLCK_LSB);
-       return stamp | (u64)tr32(TG3_EAV_REF_CLCK_MSB) << 32;
+       u64 stamp;
+
+       ptp_read_system_prets(sts);
+       stamp = tr32(TG3_EAV_REF_CLCK_LSB);
+       ptp_read_system_postts(sts);
+       stamp |= (u64)tr32(TG3_EAV_REF_CLCK_MSB) << 32;
+
+       return stamp;
 }
 
 /* tp->lock must be held */
@@ -6229,13 +6240,14 @@ static int tg3_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
        return 0;
 }
 
-static int tg3_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int tg3_ptp_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
+                           struct ptp_system_timestamp *sts)
 {
        u64 ns;
        struct tg3 *tp = container_of(ptp, struct tg3, ptp_info);
 
        tg3_full_lock(tp, 0);
-       ns = tg3_refclk_read(tp);
+       ns = tg3_refclk_read(tp, sts);
        ns += tp->ptp_adjust;
        tg3_full_unlock(tp);
 
@@ -6330,7 +6342,7 @@ static const struct ptp_clock_info tg3_ptp_caps = {
        .pps            = 0,
        .adjfreq        = tg3_ptp_adjfreq,
        .adjtime        = tg3_ptp_adjtime,
-       .gettime64      = tg3_ptp_gettime,
+       .gettimex64     = tg3_ptp_gettimex,
        .settime64      = tg3_ptp_settime,
        .enable         = tg3_ptp_enable,
 };
@@ -16973,32 +16985,6 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
        return err;
 }
 
-#ifdef CONFIG_SPARC
-static int tg3_get_macaddr_sparc(struct tg3 *tp)
-{
-       struct net_device *dev = tp->dev;
-       struct pci_dev *pdev = tp->pdev;
-       struct device_node *dp = pci_device_to_OF_node(pdev);
-       const unsigned char *addr;
-       int len;
-
-       addr = of_get_property(dp, "local-mac-address", &len);
-       if (addr && len == ETH_ALEN) {
-               memcpy(dev->dev_addr, addr, ETH_ALEN);
-               return 0;
-       }
-       return -ENODEV;
-}
-
-static int tg3_get_default_macaddr_sparc(struct tg3 *tp)
-{
-       struct net_device *dev = tp->dev;
-
-       memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
-       return 0;
-}
-#endif
-
 static int tg3_get_device_address(struct tg3 *tp)
 {
        struct net_device *dev = tp->dev;
@@ -17006,10 +16992,8 @@ static int tg3_get_device_address(struct tg3 *tp)
        int addr_ok = 0;
        int err;
 
-#ifdef CONFIG_SPARC
-       if (!tg3_get_macaddr_sparc(tp))
+       if (!eth_platform_get_mac_address(&tp->pdev->dev, dev->dev_addr))
                return 0;
-#endif
 
        if (tg3_flag(tp, IS_SSB_CORE)) {
                err = ssb_gige_get_macaddr(tp->pdev, &dev->dev_addr[0]);
@@ -17071,13 +17055,8 @@ static int tg3_get_device_address(struct tg3 *tp)
                }
        }
 
-       if (!is_valid_ether_addr(&dev->dev_addr[0])) {
-#ifdef CONFIG_SPARC
-               if (!tg3_get_default_macaddr_sparc(tp))
-                       return 0;
-#endif
+       if (!is_valid_ether_addr(&dev->dev_addr[0]))
                return -EINVAL;
-       }
        return 0;
 }
 
index 1d86b4d..d9a208f 100644 (file)
@@ -4055,7 +4055,7 @@ static int macb_probe(struct platform_device *pdev)
        if (mac) {
                ether_addr_copy(bp->dev->dev_addr, mac);
        } else {
-               err = of_get_nvmem_mac_address(np, bp->dev->dev_addr);
+               err = nvmem_get_mac_address(&pdev->dev, bp->dev->dev_addr);
                if (err) {
                        if (err == -EPROBE_DEFER)
                                goto err_out_free_netdev;
index 6aeb104..73632b8 100644 (file)
@@ -277,10 +277,6 @@ static int cavium_ptp_probe(struct pci_dev *pdev,
        writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
 
        clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
-       if (!clock->ptp_clock) {
-               err = -ENODEV;
-               goto error_stop;
-       }
        if (IS_ERR(clock->ptp_clock)) {
                err = PTR_ERR(clock->ptp_clock);
                goto error_stop;
index 4b3aecf..5359c10 100644 (file)
@@ -1080,8 +1080,11 @@ static int octeon_mgmt_open(struct net_device *netdev)
        /* Set the mode of the interface, RGMII/MII. */
        if (OCTEON_IS_MODEL(OCTEON_CN6XXX) && netdev->phydev) {
                union cvmx_agl_prtx_ctl agl_prtx_ctl;
-               int rgmii_mode = (netdev->phydev->supported &
-                                 (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) != 0;
+               int rgmii_mode =
+                       (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                          netdev->phydev->supported) |
+                        linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                          netdev->phydev->supported)) != 0;
 
                agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
                agl_prtx_ctl.s.mode = rgmii_mode ? 0 : 1;
index e2cdfa7..e8001e9 100644 (file)
@@ -24,7 +24,8 @@ config CHELSIO_T1
        ---help---
          This driver supports Chelsio gigabit and 10-gigabit
          Ethernet cards. More information about adapter features and
-         performance tuning is in <file:Documentation/networking/cxgb.txt>.
+         performance tuning is in
+         <file:Documentation/networking/device_drivers/chelsio/cxgb.txt>.
 
          For general information about Chelsio and our products, visit
          our website at <http://www.chelsio.com>.
index b16f4b3..2d1ca92 100644 (file)
@@ -404,6 +404,7 @@ struct adapter_params {
        bool fr_nsmr_tpte_wr_support;     /* FW support for FR_NSMR_TPTE_WR */
        u8 fw_caps_support;             /* 32-bit Port Capabilities */
        bool filter2_wr_support;        /* FW support for FILTER2_WR */
+       unsigned int viid_smt_extn_support:1; /* FW returns vin and smt index */
 
        /* MPS Buffer Group Map[per Port].  Bit i is set if buffer group i is
         * used by the Port
@@ -592,6 +593,13 @@ struct port_info {
        bool ptp_enable;
        struct sched_table *sched_tbl;
        u32 eth_flags;
+
+       /* viid and smt fields either returned by fw
+        * or decoded by parsing viid by driver.
+        */
+       u8 vin;
+       u8 vivld;
+       u8 smt_idx;
 };
 
 struct dentry;
@@ -1757,7 +1765,7 @@ int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf,
                unsigned int nexact, unsigned int rcaps, unsigned int wxcaps);
 int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
                unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac,
-               unsigned int *rss_size);
+               unsigned int *rss_size, u8 *vivld, u8 *vin);
 int t4_free_vi(struct adapter *adap, unsigned int mbox,
               unsigned int pf, unsigned int vf,
               unsigned int viid);
@@ -1783,7 +1791,7 @@ int t4_free_mac_filt(struct adapter *adap, unsigned int mbox,
                     unsigned int viid, unsigned int naddr,
                     const u8 **addr, bool sleep_ok);
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
-                 int idx, const u8 *addr, bool persist, bool add_smt);
+                 int idx, const u8 *addr, bool persist, u8 *smt_idx);
 int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid,
                     bool ucast, u64 vec, bool sleep_ok);
 int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
index d49db46..6ba9099 100644 (file)
@@ -453,7 +453,7 @@ static int link_start(struct net_device *dev)
        if (ret == 0) {
                ret = t4_change_mac(pi->adapter, mb, pi->viid,
                                    pi->xact_addr_filt, dev->dev_addr, true,
-                                   true);
+                                   &pi->smt_idx);
                if (ret >= 0) {
                        pi->xact_addr_filt = ret;
                        ret = 0;
@@ -1585,28 +1585,6 @@ unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
 EXPORT_SYMBOL(cxgb4_best_aligned_mtu);
 
 /**
- *     cxgb4_tp_smt_idx - Get the Source Mac Table index for this VI
- *     @chip: chip type
- *     @viid: VI id of the given port
- *
- *     Return the SMT index for this VI.
- */
-unsigned int cxgb4_tp_smt_idx(enum chip_type chip, unsigned int viid)
-{
-       /* In T4/T5, SMT contains 256 SMAC entries organized in
-        * 128 rows of 2 entries each.
-        * In T6, SMT contains 256 SMAC entries in 256 rows.
-        * TODO: The below code needs to be updated when we add support
-        * for 256 VFs.
-        */
-       if (CHELSIO_CHIP_VERSION(chip) <= CHELSIO_T5)
-               return ((viid & 0x7f) << 1);
-       else
-               return (viid & 0x7f);
-}
-EXPORT_SYMBOL(cxgb4_tp_smt_idx);
-
-/**
  *     cxgb4_port_chan - get the HW channel of a port
  *     @dev: the net device for the port
  *
@@ -2280,8 +2258,6 @@ static int cxgb_up(struct adapter *adap)
 #if IS_ENABLED(CONFIG_IPV6)
        update_clip(adap);
 #endif
-       /* Initialize hash mac addr list*/
-       INIT_LIST_HEAD(&adap->mac_hlist);
        return err;
 
  irq_err:
@@ -2303,6 +2279,7 @@ static void cxgb_down(struct adapter *adapter)
 
        t4_sge_stop(adapter);
        t4_free_sge_resources(adapter);
+
        adapter->flags &= ~FULL_INIT_DONE;
 }
 
@@ -2669,7 +2646,7 @@ static void cxgb4_mgmt_fill_vf_station_mac_addr(struct adapter *adap)
 
        for (vf = 0, nvfs = pci_sriov_get_totalvfs(adap->pdev);
                vf < nvfs; vf++) {
-               macaddr[5] = adap->pf * 16 + vf;
+               macaddr[5] = adap->pf * nvfs + vf;
                ether_addr_copy(adap->vfinfo[vf].vf_mac_addr, macaddr);
        }
 }
@@ -2863,7 +2840,8 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p)
                return -EADDRNOTAVAIL;
 
        ret = t4_change_mac(pi->adapter, pi->adapter->pf, pi->viid,
-                           pi->xact_addr_filt, addr->sa_data, true, true);
+                           pi->xact_addr_filt, addr->sa_data, true,
+                           &pi->smt_idx);
        if (ret < 0)
                return ret;
 
@@ -4467,6 +4445,15 @@ static int adap_init0(struct adapter *adap)
                adap->params.filter2_wr_support = (ret == 0 && val[0] != 0);
        }
 
+       /* Check if FW supports returning vin and smt index.
+        * If this is not supported, driver will interpret
+        * these values from viid.
+        */
+       params[0] = FW_PARAM_DEV(OPAQUE_VIID_SMT_EXTN);
+       ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+                             1, params, val);
+       adap->params.viid_smt_extn_support = (ret == 0 && val[0] != 0);
+
        /*
         * Get device capabilities so we can determine what resources we need
         * to manage.
@@ -4777,14 +4764,26 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
 
        for_each_port(adap, i) {
-               struct port_info *p = adap2pinfo(adap, i);
+               struct port_info *pi = adap2pinfo(adap, i);
+               u8 vivld = 0, vin = 0;
 
-               ret = t4_alloc_vi(adap, adap->mbox, p->tx_chan, adap->pf, 0, 1,
-                                 NULL, NULL);
+               ret = t4_alloc_vi(adap, adap->mbox, pi->tx_chan, adap->pf, 0, 1,
+                                 NULL, NULL, &vivld, &vin);
                if (ret < 0)
                        return PCI_ERS_RESULT_DISCONNECT;
-               p->viid = ret;
-               p->xact_addr_filt = -1;
+               pi->viid = ret;
+               pi->xact_addr_filt = -1;
+               /* If fw supports returning the VIN as part of FW_VI_CMD,
+                * save the returned values.
+                */
+               if (adap->params.viid_smt_extn_support) {
+                       pi->vivld = vivld;
+                       pi->vin = vin;
+               } else {
+                       /* Retrieve the values from VIID */
+                       pi->vivld = FW_VIID_VIVLD_G(pi->viid);
+                       pi->vin = FW_VIID_VIN_G(pi->viid);
+               }
        }
 
        t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
@@ -5621,6 +5620,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                             (is_t5(adapter->params.chip) ? STATMODE_V(0) :
                              T6_STATMODE_V(0)));
 
+       /* Initialize hash mac addr list */
+       INIT_LIST_HEAD(&adapter->mac_hlist);
+
        for_each_port(adapter, i) {
                netdev = alloc_etherdev_mq(sizeof(struct port_info),
                                           MAX_ETH_QSETS);
@@ -5899,6 +5901,7 @@ fw_attach_fail:
 static void remove_one(struct pci_dev *pdev)
 {
        struct adapter *adapter = pci_get_drvdata(pdev);
+       struct hash_mac_addr *entry, *tmp;
 
        if (!adapter) {
                pci_release_regions(pdev);
@@ -5948,6 +5951,12 @@ static void remove_one(struct pci_dev *pdev)
                if (adapter->num_uld || adapter->num_ofld_uld)
                        t4_uld_mem_free(adapter);
                free_some_resources(adapter);
+               list_for_each_entry_safe(entry, tmp, &adapter->mac_hlist,
+                                        list) {
+                       list_del(&entry->list);
+                       kfree(entry);
+               }
+
 #if IS_ENABLED(CONFIG_IPV6)
                t4_cleanup_clip_tbl(adapter);
 #endif
index 99022c0..4852feb 100644 (file)
@@ -495,14 +495,11 @@ u64 cxgb4_select_ntuple(struct net_device *dev,
                ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift;
 
        if (tp->vnic_shift >= 0 && (tp->ingress_config & VNIC_F)) {
-               u32 viid = cxgb4_port_viid(dev);
-               u32 vf = FW_VIID_VIN_G(viid);
-               u32 pf = FW_VIID_PFN_G(viid);
-               u32 vld = FW_VIID_VIVLD_G(viid);
-
-               ntuple |= (u64)(FT_VNID_ID_VF_V(vf) |
-                               FT_VNID_ID_PF_V(pf) |
-                               FT_VNID_ID_VLD_V(vld)) << tp->vnic_shift;
+               struct port_info *pi = (struct port_info *)netdev_priv(dev);
+
+               ntuple |= (u64)(FT_VNID_ID_VF_V(pi->vin) |
+                               FT_VNID_ID_PF_V(adap->pf) |
+                               FT_VNID_ID_VLD_V(pi->vivld)) << tp->vnic_shift;
        }
 
        return ntuple;
index cb52394..e8c3429 100644 (file)
@@ -5880,7 +5880,6 @@ int t4_set_trace_filter(struct adapter *adap, const struct trace_params *tp,
 {
        int i, ofst = idx * 4;
        u32 data_reg, mask_reg, cfg;
-       u32 multitrc = TRCMULTIFILTER_F;
 
        if (!enable) {
                t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A_A + ofst, 0);
@@ -5900,7 +5899,6 @@ int t4_set_trace_filter(struct adapter *adap, const struct trace_params *tp,
                 * maximum packet capture size of 9600 bytes is recommended.
                 * Also in this mode, only trace0 can be enabled and running.
                 */
-               multitrc = 0;
                if (tp->snap_len > 9600 || idx)
                        return -EINVAL;
        }
@@ -7141,21 +7139,10 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
                         unsigned int cache_line_size)
 {
        unsigned int page_shift = fls(page_size) - 1;
-       unsigned int sge_hps = page_shift - 10;
        unsigned int stat_len = cache_line_size > 64 ? 128 : 64;
        unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size;
        unsigned int fl_align_log = fls(fl_align) - 1;
 
-       t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A,
-                    HOSTPAGESIZEPF0_V(sge_hps) |
-                    HOSTPAGESIZEPF1_V(sge_hps) |
-                    HOSTPAGESIZEPF2_V(sge_hps) |
-                    HOSTPAGESIZEPF3_V(sge_hps) |
-                    HOSTPAGESIZEPF4_V(sge_hps) |
-                    HOSTPAGESIZEPF5_V(sge_hps) |
-                    HOSTPAGESIZEPF6_V(sge_hps) |
-                    HOSTPAGESIZEPF7_V(sge_hps));
-
        if (is_t4(adap->params.chip)) {
                t4_set_reg_field(adap, SGE_CONTROL_A,
                                 INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
@@ -7488,7 +7475,7 @@ int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf,
  */
 int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
                unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac,
-               unsigned int *rss_size)
+               unsigned int *rss_size, u8 *vivld, u8 *vin)
 {
        int ret;
        struct fw_vi_cmd c;
@@ -7523,6 +7510,13 @@ int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port,
        }
        if (rss_size)
                *rss_size = FW_VI_CMD_RSSSIZE_G(be16_to_cpu(c.rsssize_pkd));
+
+       if (vivld)
+               *vivld = FW_VI_CMD_VFVLD_G(be32_to_cpu(c.alloc_to_len16));
+
+       if (vin)
+               *vin = FW_VI_CMD_VIN_G(be32_to_cpu(c.alloc_to_len16));
+
        return FW_VI_CMD_VIID_G(be16_to_cpu(c.type_viid));
 }
 
@@ -7980,7 +7974,7 @@ int t4_free_mac_filt(struct adapter *adap, unsigned int mbox,
  *     MAC value.
  */
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
-                 int idx, const u8 *addr, bool persist, bool add_smt)
+                 int idx, const u8 *addr, bool persist, u8 *smt_idx)
 {
        int ret, mode;
        struct fw_vi_mac_cmd c;
@@ -7989,7 +7983,7 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
 
        if (idx < 0)                             /* new allocation */
                idx = persist ? FW_VI_MAC_ADD_PERSIST_MAC : FW_VI_MAC_ADD_MAC;
-       mode = add_smt ? FW_VI_MAC_SMT_AND_MPSTCAM : FW_VI_MAC_MPS_TCAM_ENTRY;
+       mode = smt_idx ? FW_VI_MAC_SMT_AND_MPSTCAM : FW_VI_MAC_MPS_TCAM_ENTRY;
 
        memset(&c, 0, sizeof(c));
        c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
@@ -8006,6 +8000,23 @@ int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
                ret = FW_VI_MAC_CMD_IDX_G(be16_to_cpu(p->valid_to_idx));
                if (ret >= max_mac_addr)
                        ret = -ENOMEM;
+               if (smt_idx) {
+                       if (adap->params.viid_smt_extn_support) {
+                               *smt_idx = FW_VI_MAC_CMD_SMTID_G
+                                                   (be32_to_cpu(c.op_to_viid));
+                       } else {
+                               /* In T4/T5, SMT contains 256 SMAC entries
+                                * organized in 128 rows of 2 entries each.
+                                * In T6, SMT contains 256 SMAC entries in
+                                * 256 rows.
+                                */
+                               if (CHELSIO_CHIP_VERSION(adap->params.chip) <=
+                                                                    CHELSIO_T5)
+                                       *smt_idx = (viid & FW_VIID_VIN_M) << 1;
+                               else
+                                       *smt_idx = (viid & FW_VIID_VIN_M);
+                       }
+               }
        }
        return ret;
 }
@@ -8593,7 +8604,7 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
 {
        unsigned int fw_caps = pi->adapter->params.fw_caps_support;
        struct fw_port_cmd port_cmd;
-       unsigned int action, link_ok, speed, mtu;
+       unsigned int action, link_ok, mtu;
        fw_port_cap32_t linkattr;
        int ret;
 
@@ -8627,7 +8638,6 @@ int t4_get_link_params(struct port_info *pi, unsigned int *link_okp,
                mtu = FW_PORT_CMD_MTU32_G(
                        be32_to_cpu(port_cmd.u.info32.auxlinfo32_mtu32));
        }
-       speed = fwcap_to_speed(linkattr);
 
        *link_okp = link_ok;
        *speedp = fwcap_to_speed(linkattr);
@@ -9374,6 +9384,7 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
        enum fw_port_type port_type;
        int mdio_addr;
        fw_port_cap32_t pcaps, acaps;
+       u8 vivld = 0, vin = 0;
        int ret;
 
        /* If we haven't yet determined whether we're talking to Firmware
@@ -9428,7 +9439,8 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
                acaps = be32_to_cpu(cmd.u.info32.acaps32);
        }
 
-       ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size);
+       ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size,
+                         &vivld, &vin);
        if (ret < 0)
                return ret;
 
@@ -9437,6 +9449,18 @@ int t4_init_portinfo(struct port_info *pi, int mbox,
        pi->lport = port;
        pi->rss_size = rss_size;
 
+       /* If fw supports returning the VIN as part of FW_VI_CMD,
+        * save the returned values.
+        */
+       if (adapter->params.viid_smt_extn_support) {
+               pi->vivld = vivld;
+               pi->vin = vin;
+       } else {
+               /* Retrieve the values from VIID */
+               pi->vivld = FW_VIID_VIVLD_G(pi->viid);
+               pi->vin =  FW_VIID_VIN_G(pi->viid);
+       }
+
        pi->port_type = port_type;
        pi->mdio_addr = mdio_addr;
        pi->mod_type = FW_PORT_MOD_TYPE_NA;
index 60df66f..bf7325f 100644 (file)
@@ -217,6 +217,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
        CH_PCI_ID_TABLE_FENTRY(0x6087), /* Custom T6225-CR */
        CH_PCI_ID_TABLE_FENTRY(0x6088), /* Custom T62100-CR */
        CH_PCI_ID_TABLE_FENTRY(0x6089), /* Custom T62100-KR */
+       CH_PCI_ID_TABLE_FENTRY(0x608a), /* Custom T62100-CR */
 CH_PCI_DEVICE_ID_TABLE_DEFINE_END;
 
 #endif /* __T4_PCI_ID_TBL_H__ */
index 57584ab..1d9b3e1 100644 (file)
@@ -1253,6 +1253,7 @@ enum fw_params_param_dev {
        FW_PARAMS_PARAM_DEV_HMA_SIZE    = 0x20,
        FW_PARAMS_PARAM_DEV_RDMA_WRITE_WITH_IMM = 0x21,
        FW_PARAMS_PARAM_DEV_RI_WRITE_CMPL_WR    = 0x24,
+       FW_PARAMS_PARAM_DEV_OPAQUE_VIID_SMT_EXTN = 0x27,
 };
 
 /*
@@ -2109,6 +2110,19 @@ struct fw_vi_cmd {
 #define FW_VI_CMD_FREE_V(x)    ((x) << FW_VI_CMD_FREE_S)
 #define FW_VI_CMD_FREE_F       FW_VI_CMD_FREE_V(1U)
 
+#define FW_VI_CMD_VFVLD_S      24
+#define FW_VI_CMD_VFVLD_M      0x1
+#define FW_VI_CMD_VFVLD_V(x)   ((x) << FW_VI_CMD_VFVLD_S)
+#define FW_VI_CMD_VFVLD_G(x)   \
+       (((x) >> FW_VI_CMD_VFVLD_S) & FW_VI_CMD_VFVLD_M)
+#define FW_VI_CMD_VFVLD_F      FW_VI_CMD_VFVLD_V(1U)
+
+#define FW_VI_CMD_VIN_S                16
+#define FW_VI_CMD_VIN_M                0xff
+#define FW_VI_CMD_VIN_V(x)     ((x) << FW_VI_CMD_VIN_S)
+#define FW_VI_CMD_VIN_G(x)     \
+       (((x) >> FW_VI_CMD_VIN_S) & FW_VI_CMD_VIN_M)
+
 #define FW_VI_CMD_VIID_S       0
 #define FW_VI_CMD_VIID_M       0xfff
 #define FW_VI_CMD_VIID_V(x)    ((x) << FW_VI_CMD_VIID_S)
@@ -2182,6 +2196,12 @@ struct fw_vi_mac_cmd {
        } u;
 };
 
+#define FW_VI_MAC_CMD_SMTID_S          12
+#define FW_VI_MAC_CMD_SMTID_M          0xff
+#define FW_VI_MAC_CMD_SMTID_V(x)       ((x) << FW_VI_MAC_CMD_SMTID_S)
+#define FW_VI_MAC_CMD_SMTID_G(x)       \
+       (((x) >> FW_VI_MAC_CMD_SMTID_S) & FW_VI_MAC_CMD_SMTID_M)
+
 #define FW_VI_MAC_CMD_VIID_S   0
 #define FW_VI_MAC_CMD_VIID_V(x)        ((x) << FW_VI_MAC_CMD_VIID_S)
 
index ff84791..8a2ad6b 100644 (file)
@@ -722,6 +722,7 @@ static int adapter_up(struct adapter *adapter)
 
                if (adapter->flags & USING_MSIX)
                        name_msix_vecs(adapter);
+
                adapter->flags |= FULL_INIT_DONE;
        }
 
@@ -747,8 +748,6 @@ static int adapter_up(struct adapter *adapter)
        enable_rx(adapter);
        t4vf_sge_start(adapter);
 
-       /* Initialize hash mac addr list*/
-       INIT_LIST_HEAD(&adapter->mac_hlist);
        return 0;
 }
 
@@ -3036,6 +3035,9 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
        if (err)
                goto err_unmap_bar;
 
+       /* Initialize hash mac addr list */
+       INIT_LIST_HEAD(&adapter->mac_hlist);
+
        /*
         * Allocate our "adapter ports" and stitch everything together.
         */
@@ -3287,6 +3289,7 @@ err_disable_device:
 static void cxgb4vf_pci_remove(struct pci_dev *pdev)
 {
        struct adapter *adapter = pci_get_drvdata(pdev);
+       struct hash_mac_addr *entry, *tmp;
 
        /*
         * Tear down driver state associated with device.
@@ -3337,6 +3340,11 @@ static void cxgb4vf_pci_remove(struct pci_dev *pdev)
                if (!is_t4(adapter->params.chip))
                        iounmap(adapter->bar2);
                kfree(adapter->mbox_log);
+               list_for_each_entry_safe(entry, tmp, &adapter->mac_hlist,
+                                        list) {
+                       list_del(&entry->list);
+                       kfree(entry);
+               }
                kfree(adapter);
        }
 
index ec0b545..e9a0213 100644 (file)
@@ -23,7 +23,7 @@ config CS89x0
        ---help---
          Support for CS89x0 chipset based Ethernet cards. If you have a
          network (Ethernet) card of this type, say Y and read the file
-         <file:Documentation/networking/cs89x0.txt>.
+         <file:Documentation/networking/device_drivers/cirrus/cs89x0.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called cs89x0.
index f42f7a6..ebd5c2c 100644 (file)
@@ -241,7 +241,7 @@ static int enic_set_ringparam(struct net_device *netdev,
        }
        enic_init_vnic_resources(enic);
        if (running) {
-               err = dev_open(netdev);
+               err = dev_open(netdev, NULL);
                if (err)
                        goto err_out;
        }
index 1003201..264e9b4 100644 (file)
@@ -113,7 +113,7 @@ config DE4X5
          These include the DE425, DE434, DE435, DE450 and DE500 models.  If
          you have a network card of this type, say Y.  More specific
          information is contained in
-         <file:Documentation/networking/de4x5.txt>.
+         <file:Documentation/networking/device_drivers/dec/de4x5.txt>.
 
          To compile this driver as a module, choose M here. The module will
          be called de4x5.
@@ -137,7 +137,7 @@ config DM9102
          This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from
          Davicom (<http://www.davicom.com.tw/>).  If you have such a network
          (Ethernet) card, say Y.  Some information is contained in the file
-         <file:Documentation/networking/dmfe.txt>.
+         <file:Documentation/networking/device_drivers/dec/dmfe.txt>.
 
          To compile this driver as a module, choose M here. The module will
          be called dmfe.
index f0536b1..d8d423f 100644 (file)
@@ -1881,7 +1881,7 @@ Compile command:
 
 gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2k.c
 
-Read Documentation/networking/dl2k.txt for details.
+Read Documentation/networking/device_drivers/dlink/dl2k.txt for details.
 
 */
 
index c5ad7a4..245abf0 100644 (file)
@@ -796,7 +796,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
        u16 vlan_tag;
 
        vlan_tag = skb_vlan_tag_get(skb);
-       vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+       vlan_prio = skb_vlan_tag_get_prio(skb);
        /* If vlan priority provided by OS is NOT in available bmap */
        if (!(adapter->vlan_prio_bmap & (1 << vlan_prio)))
                vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) |
@@ -1049,30 +1049,35 @@ static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter,
                                             struct be_wrb_params
                                             *wrb_params)
 {
+       bool insert_vlan = false;
        u16 vlan_tag = 0;
 
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (unlikely(!skb))
                return skb;
 
-       if (skb_vlan_tag_present(skb))
+       if (skb_vlan_tag_present(skb)) {
                vlan_tag = be_get_tx_vlan_tag(adapter, skb);
+               insert_vlan = true;
+       }
 
        if (qnq_async_evt_rcvd(adapter) && adapter->pvid) {
-               if (!vlan_tag)
+               if (!insert_vlan) {
                        vlan_tag = adapter->pvid;
+                       insert_vlan = true;
+               }
                /* f/w workaround to set skip_hw_vlan = 1, informs the F/W to
                 * skip VLAN insertion
                 */
                BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1);
        }
 
-       if (vlan_tag) {
+       if (insert_vlan) {
                skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q),
                                                vlan_tag);
                if (unlikely(!skb))
                        return skb;
-               skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(skb);
        }
 
        /* Insert the outer VLAN, if any */
index 6e0f47f..9510c9d 100644 (file)
@@ -2475,6 +2475,7 @@ static void dpaa_adjust_link(struct net_device *net_dev)
 
 static int dpaa_phy_init(struct net_device *net_dev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        struct mac_device *mac_dev;
        struct phy_device *phy_dev;
        struct dpaa_priv *priv;
@@ -2491,7 +2492,9 @@ static int dpaa_phy_init(struct net_device *net_dev)
        }
 
        /* Remove any features not supported by the controller */
-       phy_dev->supported &= mac_dev->if_support;
+       ethtool_convert_legacy_u32_to_link_mode(mask, mac_dev->if_support);
+       linkmode_and(phy_dev->supported, phy_dev->supported, mask);
+
        phy_support_asym_pause(phy_dev);
 
        mac_dev->phy_dev = phy_dev;
index 13d6e22..6249711 100644 (file)
@@ -529,6 +529,75 @@ static int dpaa_get_ts_info(struct net_device *net_dev,
        return 0;
 }
 
+static int dpaa_get_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *c)
+{
+       struct qman_portal *portal;
+       u32 period;
+       u8 thresh;
+
+       portal = qman_get_affine_portal(smp_processor_id());
+       qman_portal_get_iperiod(portal, &period);
+       qman_dqrr_get_ithresh(portal, &thresh);
+
+       c->rx_coalesce_usecs = period;
+       c->rx_max_coalesced_frames = thresh;
+       c->use_adaptive_rx_coalesce = false;
+
+       return 0;
+}
+
+static int dpaa_set_coalesce(struct net_device *dev,
+                            struct ethtool_coalesce *c)
+{
+       const cpumask_t *cpus = qman_affine_cpus();
+       bool needs_revert[NR_CPUS] = {false};
+       struct qman_portal *portal;
+       u32 period, prev_period;
+       u8 thresh, prev_thresh;
+       int cpu, res;
+
+       if (c->use_adaptive_rx_coalesce)
+               return -EINVAL;
+
+       period = c->rx_coalesce_usecs;
+       thresh = c->rx_max_coalesced_frames;
+
+       /* save previous values */
+       portal = qman_get_affine_portal(smp_processor_id());
+       qman_portal_get_iperiod(portal, &prev_period);
+       qman_dqrr_get_ithresh(portal, &prev_thresh);
+
+       /* set new values */
+       for_each_cpu(cpu, cpus) {
+               portal = qman_get_affine_portal(cpu);
+               res = qman_portal_set_iperiod(portal, period);
+               if (res)
+                       goto revert_values;
+               res = qman_dqrr_set_ithresh(portal, thresh);
+               if (res) {
+                       qman_portal_set_iperiod(portal, prev_period);
+                       goto revert_values;
+               }
+               needs_revert[cpu] = true;
+       }
+
+       return 0;
+
+revert_values:
+       /* restore previous values */
+       for_each_cpu(cpu, cpus) {
+               if (!needs_revert[cpu])
+                       continue;
+               portal = qman_get_affine_portal(cpu);
+               /* previous values will not fail, ignore return value */
+               qman_portal_set_iperiod(portal, prev_period);
+               qman_dqrr_set_ithresh(portal, prev_thresh);
+       }
+
+       return res;
+}
+
 const struct ethtool_ops dpaa_ethtool_ops = {
        .get_drvinfo = dpaa_get_drvinfo,
        .get_msglevel = dpaa_get_msglevel,
@@ -545,4 +614,6 @@ const struct ethtool_ops dpaa_ethtool_ops = {
        .get_rxnfc = dpaa_get_rxnfc,
        .set_rxnfc = dpaa_set_rxnfc,
        .get_ts_info = dpaa_get_ts_info,
+       .get_coalesce = dpaa_get_coalesce,
+       .set_coalesce = dpaa_set_coalesce,
 };
index 88f7acc..1ca9a18 100644 (file)
@@ -13,7 +13,8 @@
 #include <linux/iommu.h>
 #include <linux/net_tstamp.h>
 #include <linux/fsl/mc.h>
-
+#include <linux/bpf.h>
+#include <linux/bpf_trace.h>
 #include <net/sock.h>
 
 #include "dpaa2-eth.h"
@@ -86,7 +87,7 @@ static void free_rx_fd(struct dpaa2_eth_priv *priv,
                addr = dpaa2_sg_get_addr(&sgt[i]);
                sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
                dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_FROM_DEVICE);
+                                DMA_BIDIRECTIONAL);
 
                skb_free_frag(sg_vaddr);
                if (dpaa2_sg_is_final(&sgt[i]))
@@ -144,7 +145,7 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
                sg_addr = dpaa2_sg_get_addr(sge);
                sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, sg_addr);
                dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_FROM_DEVICE);
+                                DMA_BIDIRECTIONAL);
 
                sg_length = dpaa2_sg_get_len(sge);
 
@@ -199,12 +200,148 @@ static struct sk_buff *build_frag_skb(struct dpaa2_eth_priv *priv,
        return skb;
 }
 
+/* Free buffers acquired from the buffer pool or which were meant to
+ * be released in the pool
+ */
+static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
+{
+       struct device *dev = priv->net_dev->dev.parent;
+       void *vaddr;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
+               dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
+                                DMA_BIDIRECTIONAL);
+               skb_free_frag(vaddr);
+       }
+}
+
+static void xdp_release_buf(struct dpaa2_eth_priv *priv,
+                           struct dpaa2_eth_channel *ch,
+                           dma_addr_t addr)
+{
+       int err;
+
+       ch->xdp.drop_bufs[ch->xdp.drop_cnt++] = addr;
+       if (ch->xdp.drop_cnt < DPAA2_ETH_BUFS_PER_CMD)
+               return;
+
+       while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid,
+                                              ch->xdp.drop_bufs,
+                                              ch->xdp.drop_cnt)) == -EBUSY)
+               cpu_relax();
+
+       if (err) {
+               free_bufs(priv, ch->xdp.drop_bufs, ch->xdp.drop_cnt);
+               ch->buf_count -= ch->xdp.drop_cnt;
+       }
+
+       ch->xdp.drop_cnt = 0;
+}
+
+static int xdp_enqueue(struct dpaa2_eth_priv *priv, struct dpaa2_fd *fd,
+                      void *buf_start, u16 queue_id)
+{
+       struct dpaa2_eth_fq *fq;
+       struct dpaa2_faead *faead;
+       u32 ctrl, frc;
+       int i, err;
+
+       /* Mark the egress frame hardware annotation area as valid */
+       frc = dpaa2_fd_get_frc(fd);
+       dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV);
+       dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL);
+
+       /* Instruct hardware to release the FD buffer directly into
+        * the buffer pool once transmission is completed, instead of
+        * sending a Tx confirmation frame to us
+        */
+       ctrl = DPAA2_FAEAD_A4V | DPAA2_FAEAD_A2V | DPAA2_FAEAD_EBDDV;
+       faead = dpaa2_get_faead(buf_start, false);
+       faead->ctrl = cpu_to_le32(ctrl);
+       faead->conf_fqid = 0;
+
+       fq = &priv->fq[queue_id];
+       for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
+               err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
+                                                 priv->tx_qdid, 0,
+                                                 fq->tx_qdbin, fd);
+               if (err != -EBUSY)
+                       break;
+       }
+
+       return err;
+}
+
+static u32 run_xdp(struct dpaa2_eth_priv *priv,
+                  struct dpaa2_eth_channel *ch,
+                  struct dpaa2_eth_fq *rx_fq,
+                  struct dpaa2_fd *fd, void *vaddr)
+{
+       dma_addr_t addr = dpaa2_fd_get_addr(fd);
+       struct rtnl_link_stats64 *percpu_stats;
+       struct bpf_prog *xdp_prog;
+       struct xdp_buff xdp;
+       u32 xdp_act = XDP_PASS;
+       int err;
+
+       percpu_stats = this_cpu_ptr(priv->percpu_stats);
+
+       rcu_read_lock();
+
+       xdp_prog = READ_ONCE(ch->xdp.prog);
+       if (!xdp_prog)
+               goto out;
+
+       xdp.data = vaddr + dpaa2_fd_get_offset(fd);
+       xdp.data_end = xdp.data + dpaa2_fd_get_len(fd);
+       xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
+       xdp_set_data_meta_invalid(&xdp);
+
+       xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
+
+       /* xdp.data pointer may have changed */
+       dpaa2_fd_set_offset(fd, xdp.data - vaddr);
+       dpaa2_fd_set_len(fd, xdp.data_end - xdp.data);
+
+       switch (xdp_act) {
+       case XDP_PASS:
+               break;
+       case XDP_TX:
+               err = xdp_enqueue(priv, fd, vaddr, rx_fq->flowid);
+               if (err) {
+                       xdp_release_buf(priv, ch, addr);
+                       percpu_stats->tx_errors++;
+                       ch->stats.xdp_tx_err++;
+               } else {
+                       percpu_stats->tx_packets++;
+                       percpu_stats->tx_bytes += dpaa2_fd_get_len(fd);
+                       ch->stats.xdp_tx++;
+               }
+               break;
+       default:
+               bpf_warn_invalid_xdp_action(xdp_act);
+               /* fall through */
+       case XDP_ABORTED:
+               trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act);
+               /* fall through */
+       case XDP_DROP:
+               xdp_release_buf(priv, ch, addr);
+               ch->stats.xdp_drop++;
+               break;
+       }
+
+out:
+       rcu_read_unlock();
+       return xdp_act;
+}
+
 /* Main Rx frame processing routine */
 static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
                         struct dpaa2_eth_channel *ch,
                         const struct dpaa2_fd *fd,
-                        struct napi_struct *napi,
-                        u16 queue_id)
+                        struct dpaa2_eth_fq *fq)
 {
        dma_addr_t addr = dpaa2_fd_get_addr(fd);
        u8 fd_format = dpaa2_fd_get_format(fd);
@@ -216,12 +353,14 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        struct dpaa2_fas *fas;
        void *buf_data;
        u32 status = 0;
+       u32 xdp_act;
 
        /* Tracing point */
        trace_dpaa2_rx_fd(priv->net_dev, fd);
 
        vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr);
-       dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE);
+       dma_sync_single_for_cpu(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                               DMA_BIDIRECTIONAL);
 
        fas = dpaa2_get_fas(vaddr, false);
        prefetch(fas);
@@ -232,8 +371,21 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        percpu_extras = this_cpu_ptr(priv->percpu_extras);
 
        if (fd_format == dpaa2_fd_single) {
+               xdp_act = run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr);
+               if (xdp_act != XDP_PASS) {
+                       percpu_stats->rx_packets++;
+                       percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
+                       return;
+               }
+
+               dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                                DMA_BIDIRECTIONAL);
                skb = build_linear_skb(ch, fd, vaddr);
        } else if (fd_format == dpaa2_fd_sg) {
+               WARN_ON(priv->xdp_prog);
+
+               dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE,
+                                DMA_BIDIRECTIONAL);
                skb = build_frag_skb(priv, ch, buf_data);
                skb_free_frag(vaddr);
                percpu_extras->rx_sg_frames++;
@@ -267,12 +419,12 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
        }
 
        skb->protocol = eth_type_trans(skb, priv->net_dev);
-       skb_record_rx_queue(skb, queue_id);
+       skb_record_rx_queue(skb, fq->flowid);
 
        percpu_stats->rx_packets++;
        percpu_stats->rx_bytes += dpaa2_fd_get_len(fd);
 
-       napi_gro_receive(napi, skb);
+       napi_gro_receive(&ch->napi, skb);
 
        return;
 
@@ -289,7 +441,7 @@ err_frame_format:
  * Observance of NAPI budget is not our concern, leaving that to the caller.
  */
 static int consume_frames(struct dpaa2_eth_channel *ch,
-                         enum dpaa2_eth_fq_type *type)
+                         struct dpaa2_eth_fq **src)
 {
        struct dpaa2_eth_priv *priv = ch->priv;
        struct dpaa2_eth_fq *fq = NULL;
@@ -312,7 +464,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch,
                fd = dpaa2_dq_fd(dq);
                fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq);
 
-               fq->consume(priv, ch, fd, &ch->napi, fq->flowid);
+               fq->consume(priv, ch, fd, fq);
                cleaned++;
        } while (!is_last);
 
@@ -320,13 +472,12 @@ static int consume_frames(struct dpaa2_eth_channel *ch,
                return 0;
 
        fq->stats.frames += cleaned;
-       ch->stats.frames += cleaned;
 
        /* A dequeue operation only pulls frames from a single queue
-        * into the store. Return the frame queue type as an out param.
+        * into the store. Return the frame queue as an out param.
         */
-       if (type)
-               *type = fq->type;
+       if (src)
+               *src = fq;
 
        return cleaned;
 }
@@ -571,8 +722,10 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
        struct rtnl_link_stats64 *percpu_stats;
        struct dpaa2_eth_drv_stats *percpu_extras;
        struct dpaa2_eth_fq *fq;
+       struct netdev_queue *nq;
        u16 queue_mapping;
        unsigned int needed_headroom;
+       u32 fd_len;
        int err, i;
 
        percpu_stats = this_cpu_ptr(priv->percpu_stats);
@@ -644,8 +797,12 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
                /* Clean up everything, including freeing the skb */
                free_tx_fd(priv, &fd);
        } else {
+               fd_len = dpaa2_fd_get_len(&fd);
                percpu_stats->tx_packets++;
-               percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
+               percpu_stats->tx_bytes += fd_len;
+
+               nq = netdev_get_tx_queue(net_dev, queue_mapping);
+               netdev_tx_sent_queue(nq, fd_len);
        }
 
        return NETDEV_TX_OK;
@@ -661,11 +818,11 @@ err_alloc_headroom:
 static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
                              struct dpaa2_eth_channel *ch __always_unused,
                              const struct dpaa2_fd *fd,
-                             struct napi_struct *napi __always_unused,
-                             u16 queue_id __always_unused)
+                             struct dpaa2_eth_fq *fq)
 {
        struct rtnl_link_stats64 *percpu_stats;
        struct dpaa2_eth_drv_stats *percpu_extras;
+       u32 fd_len = dpaa2_fd_get_len(fd);
        u32 fd_errors;
 
        /* Tracing point */
@@ -673,7 +830,10 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
 
        percpu_extras = this_cpu_ptr(priv->percpu_extras);
        percpu_extras->tx_conf_frames++;
-       percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd);
+       percpu_extras->tx_conf_bytes += fd_len;
+
+       fq->dq_frames++;
+       fq->dq_bytes += fd_len;
 
        /* Check frame errors in the FD field */
        fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK;
@@ -735,23 +895,6 @@ static int set_tx_csum(struct dpaa2_eth_priv *priv, bool enable)
        return 0;
 }
 
-/* Free buffers acquired from the buffer pool or which were meant to
- * be released in the pool
- */
-static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
-{
-       struct device *dev = priv->net_dev->dev.parent;
-       void *vaddr;
-       int i;
-
-       for (i = 0; i < count; i++) {
-               vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]);
-               dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
-                                DMA_FROM_DEVICE);
-               skb_free_frag(vaddr);
-       }
-}
-
 /* Perform a single release command to add buffers
  * to the specified buffer pool
  */
@@ -775,7 +918,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv,
                buf = PTR_ALIGN(buf, priv->rx_buf_align);
 
                addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE,
-                                     DMA_FROM_DEVICE);
+                                     DMA_BIDIRECTIONAL);
                if (unlikely(dma_mapping_error(dev, addr)))
                        goto err_map;
 
@@ -934,8 +1077,9 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
        struct dpaa2_eth_channel *ch;
        struct dpaa2_eth_priv *priv;
        int rx_cleaned = 0, txconf_cleaned = 0;
-       enum dpaa2_eth_fq_type type = 0;
-       int store_cleaned;
+       struct dpaa2_eth_fq *fq, *txc_fq = NULL;
+       struct netdev_queue *nq;
+       int store_cleaned, work_done;
        int err;
 
        ch = container_of(napi, struct dpaa2_eth_channel, napi);
@@ -949,18 +1093,25 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
                /* Refill pool if appropriate */
                refill_pool(priv, ch, priv->bpid);
 
-               store_cleaned = consume_frames(ch, &type);
-               if (type == DPAA2_RX_FQ)
+               store_cleaned = consume_frames(ch, &fq);
+               if (!store_cleaned)
+                       break;
+               if (fq->type == DPAA2_RX_FQ) {
                        rx_cleaned += store_cleaned;
-               else
+               } else {
                        txconf_cleaned += store_cleaned;
+                       /* We have a single Tx conf FQ on this channel */
+                       txc_fq = fq;
+               }
 
                /* If we either consumed the whole NAPI budget with Rx frames
                 * or we reached the Tx confirmations threshold, we're done.
                 */
                if (rx_cleaned >= budget ||
-                   txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI)
-                       return budget;
+                   txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI) {
+                       work_done = budget;
+                       goto out;
+               }
        } while (store_cleaned);
 
        /* We didn't consume the entire budget, so finish napi and
@@ -974,7 +1125,18 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
        WARN_ONCE(err, "CDAN notifications rearm failed on core %d",
                  ch->nctx.desired_cpu);
 
-       return max(rx_cleaned, 1);
+       work_done = max(rx_cleaned, 1);
+
+out:
+       if (txc_fq) {
+               nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid);
+               netdev_tx_completed_queue(nq, txc_fq->dq_frames,
+                                         txc_fq->dq_bytes);
+               txc_fq->dq_frames = 0;
+               txc_fq->dq_bytes = 0;
+       }
+
+       return work_done;
 }
 
 static void enable_ch_napi(struct dpaa2_eth_priv *priv)
@@ -1400,6 +1562,174 @@ static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        return -EINVAL;
 }
 
+static bool xdp_mtu_valid(struct dpaa2_eth_priv *priv, int mtu)
+{
+       int mfl, linear_mfl;
+
+       mfl = DPAA2_ETH_L2_MAX_FRM(mtu);
+       linear_mfl = DPAA2_ETH_RX_BUF_SIZE - DPAA2_ETH_RX_HWA_SIZE -
+                    dpaa2_eth_rx_head_room(priv) - XDP_PACKET_HEADROOM;
+
+       if (mfl > linear_mfl) {
+               netdev_warn(priv->net_dev, "Maximum MTU for XDP is %d\n",
+                           linear_mfl - VLAN_ETH_HLEN);
+               return false;
+       }
+
+       return true;
+}
+
+static int set_rx_mfl(struct dpaa2_eth_priv *priv, int mtu, bool has_xdp)
+{
+       int mfl, err;
+
+       /* We enforce a maximum Rx frame length based on MTU only if we have
+        * an XDP program attached (in order to avoid Rx S/G frames).
+        * Otherwise, we accept all incoming frames as long as they are not
+        * larger than maximum size supported in hardware
+        */
+       if (has_xdp)
+               mfl = DPAA2_ETH_L2_MAX_FRM(mtu);
+       else
+               mfl = DPAA2_ETH_MFL;
+
+       err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, mfl);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_set_max_frame_length failed\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int dpaa2_eth_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+       int err;
+
+       if (!priv->xdp_prog)
+               goto out;
+
+       if (!xdp_mtu_valid(priv, new_mtu))
+               return -EINVAL;
+
+       err = set_rx_mfl(priv, new_mtu, true);
+       if (err)
+               return err;
+
+out:
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static int update_rx_buffer_headroom(struct dpaa2_eth_priv *priv, bool has_xdp)
+{
+       struct dpni_buffer_layout buf_layout = {0};
+       int err;
+
+       err = dpni_get_buffer_layout(priv->mc_io, 0, priv->mc_token,
+                                    DPNI_QUEUE_RX, &buf_layout);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_get_buffer_layout failed\n");
+               return err;
+       }
+
+       /* Reserve extra headroom for XDP header size changes */
+       buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv) +
+                                   (has_xdp ? XDP_PACKET_HEADROOM : 0);
+       buf_layout.options = DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM;
+       err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token,
+                                    DPNI_QUEUE_RX, &buf_layout);
+       if (err) {
+               netdev_err(priv->net_dev, "dpni_set_buffer_layout failed\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int setup_xdp(struct net_device *dev, struct bpf_prog *prog)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+       struct dpaa2_eth_channel *ch;
+       struct bpf_prog *old;
+       bool up, need_update;
+       int i, err;
+
+       if (prog && !xdp_mtu_valid(priv, dev->mtu))
+               return -EINVAL;
+
+       if (prog) {
+               prog = bpf_prog_add(prog, priv->num_channels);
+               if (IS_ERR(prog))
+                       return PTR_ERR(prog);
+       }
+
+       up = netif_running(dev);
+       need_update = (!!priv->xdp_prog != !!prog);
+
+       if (up)
+               dpaa2_eth_stop(dev);
+
+       /* While in xdp mode, enforce a maximum Rx frame size based on MTU.
+        * Also, when switching between xdp/non-xdp modes we need to reconfigure
+        * our Rx buffer layout. Buffer pool was drained on dpaa2_eth_stop,
+        * so we are sure no old format buffers will be used from now on.
+        */
+       if (need_update) {
+               err = set_rx_mfl(priv, dev->mtu, !!prog);
+               if (err)
+                       goto out_err;
+               err = update_rx_buffer_headroom(priv, !!prog);
+               if (err)
+                       goto out_err;
+       }
+
+       old = xchg(&priv->xdp_prog, prog);
+       if (old)
+               bpf_prog_put(old);
+
+       for (i = 0; i < priv->num_channels; i++) {
+               ch = priv->channel[i];
+               old = xchg(&ch->xdp.prog, prog);
+               if (old)
+                       bpf_prog_put(old);
+       }
+
+       if (up) {
+               err = dpaa2_eth_open(dev);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+
+out_err:
+       if (prog)
+               bpf_prog_sub(prog, priv->num_channels);
+       if (up)
+               dpaa2_eth_open(dev);
+
+       return err;
+}
+
+static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(dev);
+
+       switch (xdp->command) {
+       case XDP_SETUP_PROG:
+               return setup_xdp(dev, xdp->prog);
+       case XDP_QUERY_PROG:
+               xdp->prog_id = priv->xdp_prog ? priv->xdp_prog->aux->id : 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_open = dpaa2_eth_open,
        .ndo_start_xmit = dpaa2_eth_tx,
@@ -1409,6 +1739,8 @@ static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_set_rx_mode = dpaa2_eth_set_rx_mode,
        .ndo_set_features = dpaa2_eth_set_features,
        .ndo_do_ioctl = dpaa2_eth_ioctl,
+       .ndo_change_mtu = dpaa2_eth_change_mtu,
+       .ndo_bpf = dpaa2_eth_xdp,
 };
 
 static void cdan_cb(struct dpaa2_io_notification_ctx *ctx)
@@ -1434,8 +1766,11 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv)
        err = fsl_mc_object_allocate(to_fsl_mc_device(dev),
                                     FSL_MC_POOL_DPCON, &dpcon);
        if (err) {
-               dev_info(dev, "Not enough DPCONs, will go on as-is\n");
-               return NULL;
+               if (err == -ENXIO)
+                       err = -EPROBE_DEFER;
+               else
+                       dev_info(dev, "Not enough DPCONs, will go on as-is\n");
+               return ERR_PTR(err);
        }
 
        err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle);
@@ -1493,8 +1828,10 @@ alloc_channel(struct dpaa2_eth_priv *priv)
                return NULL;
 
        channel->dpcon = setup_dpcon(priv);
-       if (!channel->dpcon)
+       if (IS_ERR_OR_NULL(channel->dpcon)) {
+               err = PTR_ERR(channel->dpcon);
                goto err_setup;
+       }
 
        err = dpcon_get_attributes(priv->mc_io, 0, channel->dpcon->mc_handle,
                                   &attr);
@@ -1513,7 +1850,7 @@ err_get_attr:
        free_dpcon(priv, channel->dpcon);
 err_setup:
        kfree(channel);
-       return NULL;
+       return ERR_PTR(err);
 }
 
 static void free_channel(struct dpaa2_eth_priv *priv,
@@ -1547,10 +1884,11 @@ static int setup_dpio(struct dpaa2_eth_priv *priv)
        for_each_online_cpu(i) {
                /* Try to allocate a channel */
                channel = alloc_channel(priv);
-               if (!channel) {
-                       dev_info(dev,
-                                "No affine channel for cpu %d and above\n", i);
-                       err = -ENODEV;
+               if (IS_ERR_OR_NULL(channel)) {
+                       err = PTR_ERR(channel);
+                       if (err != -EPROBE_DEFER)
+                               dev_info(dev,
+                                        "No affine channel for cpu %d and above\n", i);
                        goto err_alloc_ch;
                }
 
@@ -1597,7 +1935,7 @@ static int setup_dpio(struct dpaa2_eth_priv *priv)
                /* Stop if we already have enough channels to accommodate all
                 * RX and TX conf queues
                 */
-               if (priv->num_channels == dpaa2_eth_queue_count(priv))
+               if (priv->num_channels == priv->dpni_attrs.num_queues)
                        break;
        }
 
@@ -1608,9 +1946,12 @@ err_set_cdan:
 err_service_reg:
        free_channel(priv, channel);
 err_alloc_ch:
+       if (err == -EPROBE_DEFER)
+               return err;
+
        if (cpumask_empty(&priv->dpio_cpumask)) {
                dev_err(dev, "No cpu with an affine DPIO/DPCON\n");
-               return err;
+               return -ENODEV;
        }
 
        dev_info(dev, "Cores %*pbl available for processing ingress traffic\n",
@@ -1732,7 +2073,10 @@ static int setup_dpbp(struct dpaa2_eth_priv *priv)
        err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP,
                                     &dpbp_dev);
        if (err) {
-               dev_err(dev, "DPBP device allocation failed\n");
+               if (err == -ENXIO)
+                       err = -EPROBE_DEFER;
+               else
+                       dev_err(dev, "DPBP device allocation failed\n");
                return err;
        }
 
index 452a8e9..69c965d 100644 (file)
@@ -139,7 +139,9 @@ struct dpaa2_faead {
 };
 
 #define DPAA2_FAEAD_A2V                        0x20000000
+#define DPAA2_FAEAD_A4V                        0x08000000
 #define DPAA2_FAEAD_UPDV               0x00001000
+#define DPAA2_FAEAD_EBDDV              0x00002000
 #define DPAA2_FAEAD_UPD                        0x00000010
 
 /* Accessors for the hardware annotation fields that we use */
@@ -243,12 +245,14 @@ struct dpaa2_eth_fq_stats {
 struct dpaa2_eth_ch_stats {
        /* Volatile dequeues retried due to portal busy */
        __u64 dequeue_portal_busy;
-       /* Number of CDANs; useful to estimate avg NAPI len */
-       __u64 cdan;
-       /* Number of frames received on queues from this channel */
-       __u64 frames;
        /* Pull errors */
        __u64 pull_err;
+       /* Number of CDANs; useful to estimate avg NAPI len */
+       __u64 cdan;
+       /* XDP counters */
+       __u64 xdp_drop;
+       __u64 xdp_tx;
+       __u64 xdp_tx_err;
 };
 
 /* Maximum number of queues associated with a DPNI */
@@ -271,17 +275,24 @@ struct dpaa2_eth_fq {
        u32 tx_qdbin;
        u16 flowid;
        int target_cpu;
+       u32 dq_frames;
+       u32 dq_bytes;
        struct dpaa2_eth_channel *channel;
        enum dpaa2_eth_fq_type type;
 
        void (*consume)(struct dpaa2_eth_priv *priv,
                        struct dpaa2_eth_channel *ch,
                        const struct dpaa2_fd *fd,
-                       struct napi_struct *napi,
-                       u16 queue_id);
+                       struct dpaa2_eth_fq *fq);
        struct dpaa2_eth_fq_stats stats;
 };
 
+struct dpaa2_eth_ch_xdp {
+       struct bpf_prog *prog;
+       u64 drop_bufs[DPAA2_ETH_BUFS_PER_CMD];
+       int drop_cnt;
+};
+
 struct dpaa2_eth_channel {
        struct dpaa2_io_notification_ctx nctx;
        struct fsl_mc_device *dpcon;
@@ -293,6 +304,7 @@ struct dpaa2_eth_channel {
        struct dpaa2_eth_priv *priv;
        int buf_count;
        struct dpaa2_eth_ch_stats stats;
+       struct dpaa2_eth_ch_xdp xdp;
 };
 
 struct dpaa2_eth_dist_fields {
@@ -352,6 +364,7 @@ struct dpaa2_eth_priv {
        u64 rx_hash_fields;
        struct dpaa2_eth_cls_rule *cls_rules;
        u8 rx_cls_enabled;
+       struct bpf_prog *xdp_prog;
 };
 
 #define DPAA2_RXH_SUPPORTED    (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \
@@ -434,9 +447,10 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
               DPAA2_ETH_RX_HWA_SIZE;
 }
 
+/* We have exactly one {Rx, Tx conf} queue per channel */
 static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv)
 {
-       return priv->dpni_attrs.num_queues;
+       return priv->num_channels;
 }
 
 int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
index 26bd5a2..0c831bf 100644 (file)
@@ -45,6 +45,9 @@ static char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = {
        "[drv] dequeue portal busy",
        "[drv] channel pull errors",
        "[drv] cdan",
+       "[drv] xdp drop",
+       "[drv] xdp tx",
+       "[drv] xdp tx errors",
 };
 
 #define DPAA2_ETH_NUM_EXTRA_STATS      ARRAY_SIZE(dpaa2_ethtool_extras)
@@ -174,8 +177,6 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        int j, k, err;
        int num_cnt;
        union dpni_statistics dpni_stats;
-       u64 cdan = 0;
-       u64 portal_busy = 0, pull_err = 0;
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
        struct dpaa2_eth_drv_stats *extras;
        struct dpaa2_eth_ch_stats *ch_stats;
@@ -212,16 +213,12 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        }
        i += j;
 
-       for (j = 0; j < priv->num_channels; j++) {
-               ch_stats = &priv->channel[j]->stats;
-               cdan += ch_stats->cdan;
-               portal_busy += ch_stats->dequeue_portal_busy;
-               pull_err += ch_stats->pull_err;
+       /* Per-channel stats */
+       for (k = 0; k < priv->num_channels; k++) {
+               ch_stats = &priv->channel[k]->stats;
+               for (j = 0; j < sizeof(*ch_stats) / sizeof(__u64); j++)
+                       *((__u64 *)data + i + j) += *((__u64 *)ch_stats + j);
        }
-
-       *(data + i++) = portal_busy;
-       *(data + i++) = pull_err;
-       *(data + i++) = cdan;
 }
 
 static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
index 84b942b..9b150db 100644 (file)
@@ -140,7 +140,10 @@ static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev)
 
        err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
        if (err) {
-               dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
+               if (err == -ENXIO)
+                       err = -EPROBE_DEFER;
+               else
+                       dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
                goto err_exit;
        }
 
index d79e4e0..71f4205 100644 (file)
@@ -393,7 +393,7 @@ void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
         */
 
        /* get local capabilities */
-       lcl_adv = ethtool_adv_to_lcl_adv_t(phy_dev->advertising);
+       lcl_adv = linkmode_adv_to_lcl_adv_t(phy_dev->advertising);
 
        /* get link partner capabilities */
        rmt_adv = 0;
index 82722d0..88a396f 100644 (file)
@@ -473,7 +473,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
 
        if (data->get_tbipa) {
                for_each_child_of_node(np, tbi) {
-                       if (strcmp(tbi->type, "tbi-phy") == 0) {
+                       if (of_node_is_type(tbi, "tbi-phy")) {
                                dev_dbg(&pdev->dev, "found TBI PHY node %pOFP\n",
                                        tbi);
                                break;
index 3c8da1a..a831086 100644 (file)
@@ -720,7 +720,7 @@ static int gfar_of_group_count(struct device_node *np)
        int num = 0;
 
        for_each_available_child_of_node(np, child)
-               if (!of_node_cmp(child->name, "queue-group"))
+               if (of_node_name_eq(child, "queue-group"))
                        num++;
 
        return num;
@@ -838,7 +838,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
        /* Parse and initialize group specific information */
        if (priv->mode == MQ_MG_MODE) {
                for_each_available_child_of_node(np, child) {
-                       if (of_node_cmp(child->name, "queue-group"))
+                       if (!of_node_name_eq(child, "queue-group"))
                                continue;
 
                        err = gfar_parse_group(child, priv, model);
@@ -1784,14 +1784,20 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
  */
 static int init_phy(struct net_device *dev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        struct gfar_private *priv = netdev_priv(dev);
-       uint gigabit_support =
-               priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
-               GFAR_SUPPORTED_GBIT : 0;
        phy_interface_t interface;
        struct phy_device *phydev;
        struct ethtool_eee edata;
 
+       linkmode_set_bit_array(phy_10_100_features_array,
+                              ARRAY_SIZE(phy_10_100_features_array),
+                              mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
+       if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
+
        priv->oldlink = 0;
        priv->oldspeed = 0;
        priv->oldduplex = -1;
@@ -1809,8 +1815,8 @@ static int init_phy(struct net_device *dev)
                gfar_configure_serdes(dev);
 
        /* Remove any features not supported by the controller */
-       phydev->supported &= (GFAR_SUPPORTED | gigabit_support);
-       phydev->advertising = phydev->supported;
+       linkmode_and(phydev->supported, phydev->supported, mask);
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        /* Add support for flow control */
        phy_support_asym_pause(phydev);
@@ -3656,7 +3662,7 @@ static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv)
                if (phydev->asym_pause)
                        rmt_adv |= LPA_PAUSE_ASYM;
 
-               lcl_adv = ethtool_adv_to_lcl_adv_t(phydev->advertising);
+               lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
                flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
                if (flowctrl & FLOW_CTRL_TX)
                        val |= MACCFG1_TX_FLOW;
index 0d76e15..241325c 100644 (file)
@@ -1134,11 +1134,9 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule,
                prio = vlan_tci_prio(rule);
                prio_mask = vlan_tci_priom(rule);
 
-               if (cfi == VLAN_TAG_PRESENT && cfi_mask == VLAN_TAG_PRESENT) {
-                       vlan |= RQFPR_CFI;
-                       vlan_mask |= RQFPR_CFI;
-               } else if (cfi != VLAN_TAG_PRESENT &&
-                          cfi_mask == VLAN_TAG_PRESENT) {
+               if (cfi_mask) {
+                       if (cfi)
+                               vlan |= RQFPR_CFI;
                        vlan_mask |= RQFPR_CFI;
                }
        }
index 32e0270..2e978cb 100644 (file)
@@ -1742,12 +1742,7 @@ static int init_phy(struct net_device *dev)
        if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII)
                uec_configure_serdes(dev);
 
-       phy_set_max_speed(phydev, SPEED_100);
-
-       if (priv->max_speed == SPEED_1000)
-               phydev->supported |= ADVERTISED_1000baseT_Full;
-
-       phydev->advertising = phydev->supported;
+       phy_set_max_speed(phydev, priv->max_speed);
 
        priv->phydev = phydev;
 
index 28e9078..c62378c 100644 (file)
@@ -1163,6 +1163,7 @@ static void hns_nic_adjust_link(struct net_device *ndev)
  */
 int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
        struct phy_device *phy_dev = h->phy_dev;
        int ret;
 
@@ -1180,8 +1181,9 @@ int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h)
        if (unlikely(ret))
                return -ENODEV;
 
-       phy_dev->supported &= h->if_support;
-       phy_dev->advertising = phy_dev->supported;
+       ethtool_convert_legacy_u32_to_link_mode(supported, h->if_support);
+       linkmode_and(phy_dev->supported, phy_dev->supported, supported);
+       linkmode_copy(phy_dev->advertising, phy_dev->supported);
 
        if (h->phy_if == PHY_INTERFACE_MODE_XGMII)
                phy_dev->autoneg = false;
index 774beda..8e9b958 100644 (file)
@@ -624,7 +624,7 @@ static void hns_nic_self_test(struct net_device *ndev,
                clear_bit(NIC_STATE_TESTING, &priv->state);
 
                if (if_running)
-                       (void)dev_open(ndev);
+                       (void)dev_open(ndev, NULL);
        }
        /* Online tests aren't run; pass by default */
 
index 002534f..d01bf53 100644 (file)
@@ -9,6 +9,6 @@ obj-$(CONFIG_HNS3) += hns3vf/
 obj-$(CONFIG_HNS3) += hnae3.o
 
 obj-$(CONFIG_HNS3_ENET) += hns3.o
-hns3-objs = hns3_enet.o hns3_ethtool.o
+hns3-objs = hns3_enet.o hns3_ethtool.o hns3_debugfs.o
 
 hns3-$(CONFIG_HNS3_DCB) += hns3_dcbnl.o
index 038326c..4d9cf39 100644 (file)
@@ -36,6 +36,9 @@ enum HCLGE_MBX_OPCODE {
        HCLGE_MBX_BIND_FUNC_QUEUE,      /* (VF -> PF) bind function and queue */
        HCLGE_MBX_GET_LINK_STATUS,      /* (VF -> PF) get link status */
        HCLGE_MBX_QUEUE_RESET,          /* (VF -> PF) reset queue */
+       HCLGE_MBX_KEEP_ALIVE,           /* (VF -> PF) send keep alive cmd */
+       HCLGE_MBX_SET_ALIVE,            /* (VF -> PF) set alive state */
+       HCLGE_MBX_SET_MTU,              /* (VF -> PF) set mtu */
 };
 
 /* below are per-VF mac-vlan subcodes */
@@ -85,6 +88,12 @@ struct hclge_mbx_pf_to_vf_cmd {
        u16 msg[8];
 };
 
+struct hclge_vf_rst_cmd {
+       u8 dest_vfid;
+       u8 vf_rst;
+       u8 rsv[22];
+};
+
 /* used by VF to store the received Async responses from PF */
 struct hclgevf_mbx_arq_ring {
 #define HCLGE_MBX_MAX_ARQ_MSG_SIZE     8
index 055b406..294e725 100644 (file)
@@ -52,6 +52,7 @@
 #define HNAE3_UNIC_CLIENT_INITED_B             0x4
 #define HNAE3_ROCE_CLIENT_INITED_B             0x5
 #define HNAE3_DEV_SUPPORT_FD_B                 0x6
+#define HNAE3_DEV_SUPPORT_GRO_B                        0x7
 
 #define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) |\
                BIT(HNAE3_DEV_SUPPORT_ROCE_B))
@@ -65,6 +66,9 @@
 #define hnae3_dev_fd_supported(hdev) \
        hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B)
 
+#define hnae3_dev_gro_supported(hdev) \
+       hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B)
+
 #define ring_ptr_move_fw(ring, p) \
        ((ring)->p = ((ring)->p + 1) % (ring)->desc_num)
 #define ring_ptr_move_bw(ring, p) \
@@ -124,14 +128,23 @@ enum hnae3_reset_notify_type {
 
 enum hnae3_reset_type {
        HNAE3_VF_RESET,
+       HNAE3_VF_FUNC_RESET,
+       HNAE3_VF_PF_FUNC_RESET,
        HNAE3_VF_FULL_RESET,
+       HNAE3_FLR_RESET,
        HNAE3_FUNC_RESET,
        HNAE3_CORE_RESET,
        HNAE3_GLOBAL_RESET,
        HNAE3_IMP_RESET,
+       HNAE3_UNKNOWN_RESET,
        HNAE3_NONE_RESET,
 };
 
+enum hnae3_flr_state {
+       HNAE3_FLR_DOWN,
+       HNAE3_FLR_DONE,
+};
+
 struct hnae3_vector_info {
        u8 __iomem *io_addr;
        int vector;
@@ -162,6 +175,7 @@ struct hnae3_client_ops {
        int (*setup_tc)(struct hnae3_handle *handle, u8 tc);
        int (*reset_notify)(struct hnae3_handle *handle,
                            enum hnae3_reset_notify_type type);
+       enum hnae3_reset_type (*process_hw_error)(struct hnae3_handle *handle);
 };
 
 #define HNAE3_CLIENT_NAME_LENGTH 16
@@ -197,6 +211,10 @@ struct hnae3_ae_dev {
  *   Enable the hardware
  * stop()
  *   Disable the hardware
+ * start_client()
+ *   Inform the hclge that client has been started
+ * stop_client()
+ *   Inform the hclge that client has been stopped
  * get_status()
  *   Get the carrier state of the back channel of the handle, 1 for ok, 0 for
  *   non-ok
@@ -292,17 +310,22 @@ struct hnae3_ae_dev {
  *   Set vlan filter config of vf
  * enable_hw_strip_rxvtag()
  *   Enable/disable hardware strip vlan tag of packets received
+ * set_gro_en
+ *   Enable/disable HW GRO
  */
 struct hnae3_ae_ops {
        int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
        void (*uninit_ae_dev)(struct hnae3_ae_dev *ae_dev);
-
+       void (*flr_prepare)(struct hnae3_ae_dev *ae_dev);
+       void (*flr_done)(struct hnae3_ae_dev *ae_dev);
        int (*init_client_instance)(struct hnae3_client *client,
                                    struct hnae3_ae_dev *ae_dev);
        void (*uninit_client_instance)(struct hnae3_client *client,
                                       struct hnae3_ae_dev *ae_dev);
        int (*start)(struct hnae3_handle *handle);
        void (*stop)(struct hnae3_handle *handle);
+       int (*client_start)(struct hnae3_handle *handle);
+       void (*client_stop)(struct hnae3_handle *handle);
        int (*get_status)(struct hnae3_handle *handle);
        void (*get_ksettings_an_result)(struct hnae3_handle *handle,
                                        u8 *auto_neg, u32 *speed, u8 *duplex);
@@ -403,6 +426,8 @@ struct hnae3_ae_ops {
                                  u16 vlan, u8 qos, __be16 proto);
        int (*enable_hw_strip_rxvtag)(struct hnae3_handle *handle, bool enable);
        void (*reset_event)(struct pci_dev *pdev, struct hnae3_handle *handle);
+       void (*set_default_reset_request)(struct hnae3_ae_dev *ae_dev,
+                                         enum hnae3_reset_type rst_type);
        void (*get_channels)(struct hnae3_handle *handle,
                             struct ethtool_channels *ch);
        void (*get_tqps_and_rss_info)(struct hnae3_handle *h,
@@ -429,7 +454,12 @@ struct hnae3_ae_ops {
                                struct ethtool_rxnfc *cmd, u32 *rule_locs);
        int (*restore_fd_rules)(struct hnae3_handle *handle);
        void (*enable_fd)(struct hnae3_handle *handle, bool enable);
-       pci_ers_result_t (*process_hw_error)(struct hnae3_ae_dev *ae_dev);
+       int (*dbg_run_cmd)(struct hnae3_handle *handle, char *cmd_buf);
+       pci_ers_result_t (*handle_hw_ras_error)(struct hnae3_ae_dev *ae_dev);
+       bool (*get_hw_reset_stat)(struct hnae3_handle *handle);
+       bool (*ae_dev_resetting)(struct hnae3_handle *handle);
+       unsigned long (*ae_dev_reset_cnt)(struct hnae3_handle *handle);
+       int (*set_gro_en)(struct hnae3_handle *handle, int enable);
 };
 
 struct hnae3_dcb_ops {
@@ -488,6 +518,14 @@ struct hnae3_roce_private_info {
        void __iomem *roce_io_base;
        int base_vector;
        int num_vectors;
+
+       /* The below attributes defined for RoCE client, hnae3 gives
+        * initial values to them, and RoCE client can modify and use
+        * them.
+        */
+       unsigned long reset_state;
+       unsigned long instance_state;
+       unsigned long state;
 };
 
 struct hnae3_unic_private_info {
@@ -520,9 +558,6 @@ struct hnae3_handle {
        struct hnae3_ae_algo *ae_algo;  /* the class who provides this handle */
        u64 flags; /* Indicate the capabilities for this handle*/
 
-       unsigned long last_reset_time;
-       enum hnae3_reset_type reset_level;
-
        union {
                struct net_device *netdev; /* first member */
                struct hnae3_knic_private_info kinfo;
@@ -533,6 +568,7 @@ struct hnae3_handle {
        u32 numa_node_mask;     /* for multi-chip support */
 
        u8 netdev_flags;
+       struct dentry *hnae3_dbgfs;
 };
 
 #define hnae3_set_field(origin, mask, shift, val) \
index ea5f8a8..b6fabbb 100644 (file)
@@ -9,6 +9,9 @@ int hns3_dcbnl_ieee_getets(struct net_device *ndev, struct ieee_ets *ets)
 {
        struct hnae3_handle *h = hns3_get_handle(ndev);
 
+       if (hns3_nic_resetting(ndev))
+               return -EBUSY;
+
        if (h->kinfo.dcb_ops->ieee_getets)
                return h->kinfo.dcb_ops->ieee_getets(h, ets);
 
@@ -20,6 +23,9 @@ int hns3_dcbnl_ieee_setets(struct net_device *ndev, struct ieee_ets *ets)
 {
        struct hnae3_handle *h = hns3_get_handle(ndev);
 
+       if (hns3_nic_resetting(ndev))
+               return -EBUSY;
+
        if (h->kinfo.dcb_ops->ieee_setets)
                return h->kinfo.dcb_ops->ieee_setets(h, ets);
 
@@ -31,6 +37,9 @@ int hns3_dcbnl_ieee_getpfc(struct net_device *ndev, struct ieee_pfc *pfc)
 {
        struct hnae3_handle *h = hns3_get_handle(ndev);
 
+       if (hns3_nic_resetting(ndev))
+               return -EBUSY;
+
        if (h->kinfo.dcb_ops->ieee_getpfc)
                return h->kinfo.dcb_ops->ieee_getpfc(h, pfc);
 
@@ -42,6 +51,9 @@ int hns3_dcbnl_ieee_setpfc(struct net_device *ndev, struct ieee_pfc *pfc)
 {
        struct hnae3_handle *h = hns3_get_handle(ndev);
 
+       if (hns3_nic_resetting(ndev))
+               return -EBUSY;
+
        if (h->kinfo.dcb_ops->ieee_setpfc)
                return h->kinfo.dcb_ops->ieee_setpfc(h, pfc);
 
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
new file mode 100644 (file)
index 0000000..86d667a
--- /dev/null
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+
+#include "hnae3.h"
+#include "hns3_enet.h"
+
+#define HNS3_DBG_READ_LEN 256
+
+static struct dentry *hns3_dbgfs_root;
+
+static int hns3_dbg_queue_info(struct hnae3_handle *h, char *cmd_buf)
+{
+       struct hns3_nic_priv *priv = h->priv;
+       struct hns3_nic_ring_data *ring_data;
+       struct hns3_enet_ring *ring;
+       u32 base_add_l, base_add_h;
+       u32 queue_num, queue_max;
+       u32 value, i = 0;
+       int cnt;
+
+       if (!priv->ring_data) {
+               dev_err(&h->pdev->dev, "ring_data is NULL\n");
+               return -EFAULT;
+       }
+
+       queue_max = h->kinfo.num_tqps;
+       cnt = kstrtouint(&cmd_buf[11], 0, &queue_num);
+       if (cnt)
+               queue_num = 0;
+       else
+               queue_max = queue_num + 1;
+
+       dev_info(&h->pdev->dev, "queue info\n");
+
+       if (queue_num >= h->kinfo.num_tqps) {
+               dev_err(&h->pdev->dev,
+                       "Queue number(%u) is out of range(%u)\n", queue_num,
+                       h->kinfo.num_tqps - 1);
+               return -EINVAL;
+       }
+
+       ring_data = priv->ring_data;
+       for (i = queue_num; i < queue_max; i++) {
+               /* Each cycle needs to determine whether the instance is reset,
+                * to prevent reference to invalid memory. And need to ensure
+                * that the following code is executed within 100ms.
+                */
+               if (test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
+                   test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
+                       return -EPERM;
+
+               ring = ring_data[i + h->kinfo.num_tqps].ring;
+               base_add_h = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_RX_RING_BASEADDR_H_REG);
+               base_add_l = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_RX_RING_BASEADDR_L_REG);
+               dev_info(&h->pdev->dev, "RX(%d) BASE ADD: 0x%08x%08x\n", i,
+                        base_add_h, base_add_l);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_BD_NUM_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING BD NUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_BD_LEN_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING BD LEN: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_TAIL_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING TAIL: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_HEAD_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING HEAD: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_FBDNUM_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING FBDNUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_RX_RING_PKTNUM_RECORD_REG);
+               dev_info(&h->pdev->dev, "RX(%d) RING PKTNUM: %u\n", i, value);
+
+               ring = ring_data[i].ring;
+               base_add_h = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_TX_RING_BASEADDR_H_REG);
+               base_add_l = readl_relaxed(ring->tqp->io_base +
+                                          HNS3_RING_TX_RING_BASEADDR_L_REG);
+               dev_info(&h->pdev->dev, "TX(%d) BASE ADD: 0x%08x%08x\n", i,
+                        base_add_h, base_add_l);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_BD_NUM_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING BD NUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_TC_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING TC: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_TAIL_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING TAIL: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_HEAD_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING HEAD: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_FBDNUM_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING FBDNUM: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_OFFSET_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING OFFSET: %u\n", i, value);
+
+               value = readl_relaxed(ring->tqp->io_base +
+                                     HNS3_RING_TX_RING_PKTNUM_RECORD_REG);
+               dev_info(&h->pdev->dev, "TX(%d) RING PKTNUM: %u\n\n", i,
+                        value);
+       }
+
+       return 0;
+}
+
+static void hns3_dbg_help(struct hnae3_handle *h)
+{
+       dev_info(&h->pdev->dev, "available commands\n");
+       dev_info(&h->pdev->dev, "queue info [number]\n");
+       dev_info(&h->pdev->dev, "dump fd tcam\n");
+       dev_info(&h->pdev->dev, "dump tc\n");
+       dev_info(&h->pdev->dev, "dump tm\n");
+       dev_info(&h->pdev->dev, "dump qos pause cfg\n");
+       dev_info(&h->pdev->dev, "dump qos pri map\n");
+       dev_info(&h->pdev->dev, "dump qos buf cfg\n");
+}
+
+static ssize_t hns3_dbg_cmd_read(struct file *filp, char __user *buffer,
+                                size_t count, loff_t *ppos)
+{
+       int uncopy_bytes;
+       char *buf;
+       int len;
+
+       if (*ppos != 0)
+               return 0;
+
+       if (count < HNS3_DBG_READ_LEN)
+               return -ENOSPC;
+
+       buf = kzalloc(HNS3_DBG_READ_LEN, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len = snprintf(buf, HNS3_DBG_READ_LEN, "%s\n",
+                      "Please echo help to cmd to get help information");
+       uncopy_bytes = copy_to_user(buffer, buf, len);
+
+       kfree(buf);
+
+       if (uncopy_bytes)
+               return -EFAULT;
+
+       return (*ppos = len);
+}
+
+static ssize_t hns3_dbg_cmd_write(struct file *filp, const char __user *buffer,
+                                 size_t count, loff_t *ppos)
+{
+       struct hnae3_handle *handle = filp->private_data;
+       struct hns3_nic_priv *priv  = handle->priv;
+       char *cmd_buf, *cmd_buf_tmp;
+       int uncopied_bytes;
+       int ret = 0;
+
+       if (*ppos != 0)
+               return 0;
+
+       /* Judge if the instance is being reset. */
+       if (test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
+           test_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
+               return 0;
+
+       cmd_buf = kzalloc(count + 1, GFP_KERNEL);
+       if (!cmd_buf)
+               return count;
+
+       uncopied_bytes = copy_from_user(cmd_buf, buffer, count);
+       if (uncopied_bytes) {
+               kfree(cmd_buf);
+               return -EFAULT;
+       }
+
+       cmd_buf[count] = '\0';
+
+       cmd_buf_tmp = strchr(cmd_buf, '\n');
+       if (cmd_buf_tmp) {
+               *cmd_buf_tmp = '\0';
+               count = cmd_buf_tmp - cmd_buf + 1;
+       }
+
+       if (strncmp(cmd_buf, "help", 4) == 0)
+               hns3_dbg_help(handle);
+       else if (strncmp(cmd_buf, "queue info", 10) == 0)
+               ret = hns3_dbg_queue_info(handle, cmd_buf);
+       else if (handle->ae_algo->ops->dbg_run_cmd)
+               ret = handle->ae_algo->ops->dbg_run_cmd(handle, cmd_buf);
+
+       if (ret)
+               hns3_dbg_help(handle);
+
+       kfree(cmd_buf);
+       cmd_buf = NULL;
+
+       return count;
+}
+
+static const struct file_operations hns3_dbg_cmd_fops = {
+       .owner = THIS_MODULE,
+       .open  = simple_open,
+       .read  = hns3_dbg_cmd_read,
+       .write = hns3_dbg_cmd_write,
+};
+
+void hns3_dbg_init(struct hnae3_handle *handle)
+{
+       const char *name = pci_name(handle->pdev);
+       struct dentry *pfile;
+
+       handle->hnae3_dbgfs = debugfs_create_dir(name, hns3_dbgfs_root);
+       if (!handle->hnae3_dbgfs)
+               return;
+
+       pfile = debugfs_create_file("cmd", 0600, handle->hnae3_dbgfs, handle,
+                                   &hns3_dbg_cmd_fops);
+       if (!pfile) {
+               debugfs_remove_recursive(handle->hnae3_dbgfs);
+               handle->hnae3_dbgfs = NULL;
+               dev_warn(&handle->pdev->dev, "create file for %s fail\n",
+                        name);
+       }
+}
+
+void hns3_dbg_uninit(struct hnae3_handle *handle)
+{
+       debugfs_remove_recursive(handle->hnae3_dbgfs);
+       handle->hnae3_dbgfs = NULL;
+}
+
+void hns3_dbg_register_debugfs(const char *debugfs_dir_name)
+{
+       hns3_dbgfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+       if (!hns3_dbgfs_root) {
+               pr_warn("Register debugfs for %s fail\n", debugfs_dir_name);
+               return;
+       }
+}
+
+void hns3_dbg_unregister_debugfs(void)
+{
+       debugfs_remove_recursive(hns3_dbgfs_root);
+       hns3_dbgfs_root = NULL;
+}
index 20fcf0d..69142a3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/vermagic.h>
 #include <net/gre.h>
 #include <net/pkt_cls.h>
+#include <net/tcp.h>
 #include <net/vxlan.h>
 
 #include "hnae3.h"
@@ -312,6 +313,24 @@ static u16 hns3_get_max_available_channels(struct hnae3_handle *h)
        return min_t(u16, rss_size, max_rss_size);
 }
 
+static void hns3_tqp_enable(struct hnae3_queue *tqp)
+{
+       u32 rcb_reg;
+
+       rcb_reg = hns3_read_dev(tqp, HNS3_RING_EN_REG);
+       rcb_reg |= BIT(HNS3_RING_EN_B);
+       hns3_write_dev(tqp, HNS3_RING_EN_REG, rcb_reg);
+}
+
+static void hns3_tqp_disable(struct hnae3_queue *tqp)
+{
+       u32 rcb_reg;
+
+       rcb_reg = hns3_read_dev(tqp, HNS3_RING_EN_REG);
+       rcb_reg &= ~BIT(HNS3_RING_EN_B);
+       hns3_write_dev(tqp, HNS3_RING_EN_REG, rcb_reg);
+}
+
 static int hns3_nic_net_up(struct net_device *netdev)
 {
        struct hns3_nic_priv *priv = netdev_priv(netdev);
@@ -334,6 +353,10 @@ static int hns3_nic_net_up(struct net_device *netdev)
        for (i = 0; i < priv->vector_num; i++)
                hns3_vector_enable(&priv->tqp_vector[i]);
 
+       /* enable rcb */
+       for (j = 0; j < h->kinfo.num_tqps; j++)
+               hns3_tqp_enable(h->kinfo.tqp[j]);
+
        /* start the ae_dev */
        ret = h->ae_algo->ops->start ? h->ae_algo->ops->start(h) : 0;
        if (ret)
@@ -344,6 +367,9 @@ static int hns3_nic_net_up(struct net_device *netdev)
        return 0;
 
 out_start_err:
+       while (j--)
+               hns3_tqp_disable(h->kinfo.tqp[j]);
+
        for (j = i - 1; j >= 0; j--)
                hns3_vector_disable(&priv->tqp_vector[j]);
 
@@ -354,11 +380,13 @@ out_start_err:
 
 static int hns3_nic_net_open(struct net_device *netdev)
 {
-       struct hns3_nic_priv *priv = netdev_priv(netdev);
        struct hnae3_handle *h = hns3_get_handle(netdev);
        struct hnae3_knic_private_info *kinfo;
        int i, ret;
 
+       if (hns3_nic_resetting(netdev))
+               return -EBUSY;
+
        netif_carrier_off(netdev);
 
        ret = hns3_nic_set_real_num_queue(netdev);
@@ -378,23 +406,24 @@ static int hns3_nic_net_open(struct net_device *netdev)
                                       kinfo->prio_tc[i]);
        }
 
-       priv->ae_handle->last_reset_time = jiffies;
        return 0;
 }
 
 static void hns3_nic_net_down(struct net_device *netdev)
 {
        struct hns3_nic_priv *priv = netdev_priv(netdev);
+       struct hnae3_handle *h = hns3_get_handle(netdev);
        const struct hnae3_ae_ops *ops;
        int i;
 
-       if (test_and_set_bit(HNS3_NIC_STATE_DOWN, &priv->state))
-               return;
-
        /* disable vectors */
        for (i = 0; i < priv->vector_num; i++)
                hns3_vector_disable(&priv->tqp_vector[i]);
 
+       /* disable rcb */
+       for (i = 0; i < h->kinfo.num_tqps; i++)
+               hns3_tqp_disable(h->kinfo.tqp[i]);
+
        /* stop ae_dev */
        ops = priv->ae_handle->ae_algo->ops;
        if (ops->stop)
@@ -408,6 +437,11 @@ static void hns3_nic_net_down(struct net_device *netdev)
 
 static int hns3_nic_net_stop(struct net_device *netdev)
 {
+       struct hns3_nic_priv *priv = netdev_priv(netdev);
+
+       if (test_and_set_bit(HNS3_NIC_STATE_DOWN, &priv->state))
+               return 0;
+
        netif_tx_stop_all_queues(netdev);
        netif_carrier_off(netdev);
 
@@ -1312,6 +1346,15 @@ static int hns3_nic_set_features(struct net_device *netdev,
                        priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx;
        }
 
+       if (changed & (NETIF_F_GRO_HW) && h->ae_algo->ops->set_gro_en) {
+               if (features & NETIF_F_GRO_HW)
+                       ret = h->ae_algo->ops->set_gro_en(h, true);
+               else
+                       ret = h->ae_algo->ops->set_gro_en(h, false);
+               if (ret)
+                       return ret;
+       }
+
        if ((changed & NETIF_F_HW_VLAN_CTAG_FILTER) &&
            h->ae_algo->ops->enable_vlan_filter) {
                if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
@@ -1530,18 +1573,11 @@ static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
 static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
-       bool if_running = netif_running(netdev);
        int ret;
 
        if (!h->ae_algo->ops->set_mtu)
                return -EOPNOTSUPP;
 
-       /* if this was called with netdev up then bring netdevice down */
-       if (if_running) {
-               (void)hns3_nic_net_stop(netdev);
-               msleep(100);
-       }
-
        ret = h->ae_algo->ops->set_mtu(h, new_mtu);
        if (ret)
                netdev_err(netdev, "failed to change MTU in hardware %d\n",
@@ -1549,10 +1585,6 @@ static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
        else
                netdev->mtu = new_mtu;
 
-       /* if the netdev was running earlier, bring it up again */
-       if (if_running && hns3_nic_net_open(netdev))
-               ret = -EINVAL;
-
        return ret;
 }
 
@@ -1615,10 +1647,9 @@ static void hns3_nic_net_timeout(struct net_device *ndev)
 
        priv->tx_timeout_count++;
 
-       if (time_before(jiffies, (h->last_reset_time + ndev->watchdog_timeo)))
-               return;
-
-       /* request the reset */
+       /* request the reset, and let the hclge to determine
+        * which reset level should be done
+        */
        if (h->ae_algo->ops->reset_event)
                h->ae_algo->ops->reset_event(h->pdev, h);
 }
@@ -1682,8 +1713,10 @@ static void hns3_disable_sriov(struct pci_dev *pdev)
 static void hns3_get_dev_capability(struct pci_dev *pdev,
                                    struct hnae3_ae_dev *ae_dev)
 {
-       if (pdev->revision >= 0x21)
+       if (pdev->revision >= 0x21) {
                hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B, 1);
+               hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B, 1);
+       }
 }
 
 /* hns3_probe - Device initialization routine
@@ -1795,8 +1828,8 @@ static pci_ers_result_t hns3_error_detected(struct pci_dev *pdev,
                return PCI_ERS_RESULT_NONE;
        }
 
-       if (ae_dev->ops->process_hw_error)
-               ret = ae_dev->ops->process_hw_error(ae_dev);
+       if (ae_dev->ops->handle_hw_ras_error)
+               ret = ae_dev->ops->handle_hw_ras_error(ae_dev);
        else
                return PCI_ERS_RESULT_NONE;
 
@@ -1819,9 +1852,29 @@ static pci_ers_result_t hns3_slot_reset(struct pci_dev *pdev)
        return PCI_ERS_RESULT_DISCONNECT;
 }
 
+static void hns3_reset_prepare(struct pci_dev *pdev)
+{
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
+
+       dev_info(&pdev->dev, "hns3 flr prepare\n");
+       if (ae_dev && ae_dev->ops && ae_dev->ops->flr_prepare)
+               ae_dev->ops->flr_prepare(ae_dev);
+}
+
+static void hns3_reset_done(struct pci_dev *pdev)
+{
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
+
+       dev_info(&pdev->dev, "hns3 flr done\n");
+       if (ae_dev && ae_dev->ops && ae_dev->ops->flr_done)
+               ae_dev->ops->flr_done(ae_dev);
+}
+
 static const struct pci_error_handlers hns3_err_handler = {
        .error_detected = hns3_error_detected,
        .slot_reset     = hns3_slot_reset,
+       .reset_prepare  = hns3_reset_prepare,
+       .reset_done     = hns3_reset_done,
 };
 
 static struct pci_driver hns3_driver = {
@@ -1875,7 +1928,9 @@ static void hns3_set_default_feature(struct net_device *netdev)
                NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
        if (pdev->revision >= 0x21) {
-               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+                       NETIF_F_GRO_HW;
+               netdev->features |= NETIF_F_GRO_HW;
 
                if (!(h->flags & HNAE3_SUPPORT_VF)) {
                        netdev->hw_features |= NETIF_F_NTUPLE;
@@ -2253,6 +2308,12 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
        if (!(netdev->features & NETIF_F_RXCSUM))
                return;
 
+       /* We MUST enable hardware checksum before enabling hardware GRO */
+       if (skb_shinfo(skb)->gso_size) {
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               return;
+       }
+
        /* check if hardware has done checksum */
        if (!hnae3_get_bit(bd_base_info, HNS3_RXD_L3L4P_B))
                return;
@@ -2296,6 +2357,9 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
 
 static void hns3_rx_skb(struct hns3_enet_ring *ring, struct sk_buff *skb)
 {
+       if (skb_has_frag_list(skb))
+               napi_gro_flush(&ring->tqp_vector->napi, false);
+
        napi_gro_receive(&ring->tqp_vector->napi, skb);
 }
 
@@ -2329,6 +2393,153 @@ static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring,
        }
 }
 
+static int hns3_alloc_skb(struct hns3_enet_ring *ring, int length,
+                         unsigned char *va)
+{
+#define HNS3_NEED_ADD_FRAG     1
+       struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean];
+       struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
+       struct sk_buff *skb;
+
+       ring->skb = napi_alloc_skb(&ring->tqp_vector->napi, HNS3_RX_HEAD_SIZE);
+       skb = ring->skb;
+       if (unlikely(!skb)) {
+               netdev_err(netdev, "alloc rx skb fail\n");
+
+               u64_stats_update_begin(&ring->syncp);
+               ring->stats.sw_err_cnt++;
+               u64_stats_update_end(&ring->syncp);
+
+               return -ENOMEM;
+       }
+
+       prefetchw(skb->data);
+
+       ring->pending_buf = 1;
+       ring->frag_num = 0;
+       ring->tail_skb = NULL;
+       if (length <= HNS3_RX_HEAD_SIZE) {
+               memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
+
+               /* We can reuse buffer as-is, just make sure it is local */
+               if (likely(page_to_nid(desc_cb->priv) == numa_node_id()))
+                       desc_cb->reuse_flag = 1;
+               else /* This page cannot be reused so discard it */
+                       put_page(desc_cb->priv);
+
+               ring_ptr_move_fw(ring, next_to_clean);
+               return 0;
+       }
+       u64_stats_update_begin(&ring->syncp);
+       ring->stats.seg_pkt_cnt++;
+       u64_stats_update_end(&ring->syncp);
+
+       ring->pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
+       __skb_put(skb, ring->pull_len);
+       hns3_nic_reuse_page(skb, ring->frag_num++, ring, ring->pull_len,
+                           desc_cb);
+       ring_ptr_move_fw(ring, next_to_clean);
+
+       return HNS3_NEED_ADD_FRAG;
+}
+
+static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
+                        struct sk_buff **out_skb, bool pending)
+{
+       struct sk_buff *skb = *out_skb;
+       struct sk_buff *head_skb = *out_skb;
+       struct sk_buff *new_skb;
+       struct hns3_desc_cb *desc_cb;
+       struct hns3_desc *pre_desc;
+       u32 bd_base_info;
+       int pre_bd;
+
+       /* if there is pending bd, the SW param next_to_clean has moved
+        * to next and the next is NULL
+        */
+       if (pending) {
+               pre_bd = (ring->next_to_clean - 1 + ring->desc_num) %
+                       ring->desc_num;
+               pre_desc = &ring->desc[pre_bd];
+               bd_base_info = le32_to_cpu(pre_desc->rx.bd_base_info);
+       } else {
+               bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
+       }
+
+       while (!hnae3_get_bit(bd_base_info, HNS3_RXD_FE_B)) {
+               desc = &ring->desc[ring->next_to_clean];
+               desc_cb = &ring->desc_cb[ring->next_to_clean];
+               bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
+               if (!hnae3_get_bit(bd_base_info, HNS3_RXD_VLD_B))
+                       return -ENXIO;
+
+               if (unlikely(ring->frag_num >= MAX_SKB_FRAGS)) {
+                       new_skb = napi_alloc_skb(&ring->tqp_vector->napi,
+                                                HNS3_RX_HEAD_SIZE);
+                       if (unlikely(!new_skb)) {
+                               netdev_err(ring->tqp->handle->kinfo.netdev,
+                                          "alloc rx skb frag fail\n");
+                               return -ENXIO;
+                       }
+                       ring->frag_num = 0;
+
+                       if (ring->tail_skb) {
+                               ring->tail_skb->next = new_skb;
+                               ring->tail_skb = new_skb;
+                       } else {
+                               skb_shinfo(skb)->frag_list = new_skb;
+                               ring->tail_skb = new_skb;
+                       }
+               }
+
+               if (ring->tail_skb) {
+                       head_skb->truesize += hnae3_buf_size(ring);
+                       head_skb->data_len += le16_to_cpu(desc->rx.size);
+                       head_skb->len += le16_to_cpu(desc->rx.size);
+                       skb = ring->tail_skb;
+               }
+
+               hns3_nic_reuse_page(skb, ring->frag_num++, ring, 0, desc_cb);
+               ring_ptr_move_fw(ring, next_to_clean);
+               ring->pending_buf++;
+       }
+
+       return 0;
+}
+
+static void hns3_set_gro_param(struct sk_buff *skb, u32 l234info,
+                              u32 bd_base_info)
+{
+       u16 gro_count;
+       u32 l3_type;
+
+       gro_count = hnae3_get_field(l234info, HNS3_RXD_GRO_COUNT_M,
+                                   HNS3_RXD_GRO_COUNT_S);
+       /* if there is no HW GRO, do not set gro params */
+       if (!gro_count)
+               return;
+
+       /* tcp_gro_complete() will copy NAPI_GRO_CB(skb)->count
+        * to skb_shinfo(skb)->gso_segs
+        */
+       NAPI_GRO_CB(skb)->count = gro_count;
+
+       l3_type = hnae3_get_field(l234info, HNS3_RXD_L3ID_M,
+                                 HNS3_RXD_L3ID_S);
+       if (l3_type == HNS3_L3_TYPE_IPV4)
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+       else if (l3_type == HNS3_L3_TYPE_IPV6)
+               skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+       else
+               return;
+
+       skb_shinfo(skb)->gso_size = hnae3_get_field(bd_base_info,
+                                                   HNS3_RXD_GRO_SIZE_M,
+                                                   HNS3_RXD_GRO_SIZE_S);
+       if (skb_shinfo(skb)->gso_size)
+               tcp_gro_complete(skb);
+}
+
 static void hns3_set_rx_skb_rss_type(struct hns3_enet_ring *ring,
                                     struct sk_buff *skb)
 {
@@ -2345,18 +2556,16 @@ static void hns3_set_rx_skb_rss_type(struct hns3_enet_ring *ring,
 }
 
 static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
-                            struct sk_buff **out_skb, int *out_bnum)
+                            struct sk_buff **out_skb)
 {
        struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
+       struct sk_buff *skb = ring->skb;
        struct hns3_desc_cb *desc_cb;
        struct hns3_desc *desc;
-       struct sk_buff *skb;
-       unsigned char *va;
        u32 bd_base_info;
-       int pull_len;
        u32 l234info;
        int length;
-       int bnum;
+       int ret;
 
        desc = &ring->desc[ring->next_to_clean];
        desc_cb = &ring->desc_cb[ring->next_to_clean];
@@ -2368,9 +2577,10 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
 
        /* Check valid BD */
        if (unlikely(!hnae3_get_bit(bd_base_info, HNS3_RXD_VLD_B)))
-               return -EFAULT;
+               return -ENXIO;
 
-       va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
+       if (!skb)
+               ring->va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
 
        /* Prefetch first cache line of first page
         * Idea is to cache few bytes of the header of the packet. Our L1 Cache
@@ -2379,62 +2589,42 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
         * lines. In such a case, single fetch would suffice to cache in the
         * relevant part of the header.
         */
-       prefetch(va);
+       prefetch(ring->va);
 #if L1_CACHE_BYTES < 128
-       prefetch(va + L1_CACHE_BYTES);
+       prefetch(ring->va + L1_CACHE_BYTES);
 #endif
 
-       skb = *out_skb = napi_alloc_skb(&ring->tqp_vector->napi,
-                                       HNS3_RX_HEAD_SIZE);
-       if (unlikely(!skb)) {
-               netdev_err(netdev, "alloc rx skb fail\n");
-
-               u64_stats_update_begin(&ring->syncp);
-               ring->stats.sw_err_cnt++;
-               u64_stats_update_end(&ring->syncp);
+       if (!skb) {
+               ret = hns3_alloc_skb(ring, length, ring->va);
+               *out_skb = skb = ring->skb;
 
-               return -ENOMEM;
-       }
-
-       prefetchw(skb->data);
-
-       bnum = 1;
-       if (length <= HNS3_RX_HEAD_SIZE) {
-               memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
-
-               /* We can reuse buffer as-is, just make sure it is local */
-               if (likely(page_to_nid(desc_cb->priv) == numa_node_id()))
-                       desc_cb->reuse_flag = 1;
-               else /* This page cannot be reused so discard it */
-                       put_page(desc_cb->priv);
+               if (ret < 0) /* alloc buffer fail */
+                       return ret;
+               if (ret > 0) { /* need add frag */
+                       ret = hns3_add_frag(ring, desc, &skb, false);
+                       if (ret)
+                               return ret;
 
-               ring_ptr_move_fw(ring, next_to_clean);
+                       /* As the head data may be changed when GRO enable, copy
+                        * the head data in after other data rx completed
+                        */
+                       memcpy(skb->data, ring->va,
+                              ALIGN(ring->pull_len, sizeof(long)));
+               }
        } else {
-               u64_stats_update_begin(&ring->syncp);
-               ring->stats.seg_pkt_cnt++;
-               u64_stats_update_end(&ring->syncp);
-
-               pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
-
-               memcpy(__skb_put(skb, pull_len), va,
-                      ALIGN(pull_len, sizeof(long)));
-
-               hns3_nic_reuse_page(skb, 0, ring, pull_len, desc_cb);
-               ring_ptr_move_fw(ring, next_to_clean);
+               ret = hns3_add_frag(ring, desc, &skb, true);
+               if (ret)
+                       return ret;
 
-               while (!hnae3_get_bit(bd_base_info, HNS3_RXD_FE_B)) {
-                       desc = &ring->desc[ring->next_to_clean];
-                       desc_cb = &ring->desc_cb[ring->next_to_clean];
-                       bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
-                       hns3_nic_reuse_page(skb, bnum, ring, 0, desc_cb);
-                       ring_ptr_move_fw(ring, next_to_clean);
-                       bnum++;
-               }
+               /* As the head data may be changed when GRO enable, copy
+                * the head data in after other data rx completed
+                */
+               memcpy(skb->data, ring->va,
+                      ALIGN(ring->pull_len, sizeof(long)));
        }
 
-       *out_bnum = bnum;
-
        l234info = le32_to_cpu(desc->rx.l234_info);
+       bd_base_info = le32_to_cpu(desc->rx.bd_base_info);
 
        /* Based on hw strategy, the tag offloaded will be stored at
         * ot_vlan_tag in two layer tag case, and stored at vlan_tag
@@ -2484,7 +2674,11 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
 
        ring->tqp_vector->rx_group.total_bytes += skb->len;
 
+       /* This is needed in order to enable forwarding support */
+       hns3_set_gro_param(skb, l234info, bd_base_info);
+
        hns3_rx_checksum(ring, skb, desc);
+       *out_skb = skb;
        hns3_set_rx_skb_rss_type(ring, skb);
 
        return 0;
@@ -2497,9 +2691,9 @@ int hns3_clean_rx_ring(
 #define RCB_NOF_ALLOC_RX_BUFF_ONCE 16
        struct net_device *netdev = ring->tqp->handle->kinfo.netdev;
        int recv_pkts, recv_bds, clean_count, err;
-       int unused_count = hns3_desc_unused(ring);
-       struct sk_buff *skb = NULL;
-       int num, bnum = 0;
+       int unused_count = hns3_desc_unused(ring) - ring->pending_buf;
+       struct sk_buff *skb = ring->skb;
+       int num;
 
        num = readl_relaxed(ring->tqp->io_base + HNS3_RING_RX_RING_FBDNUM_REG);
        rmb(); /* Make sure num taken effect before the other data is touched */
@@ -2513,24 +2707,32 @@ int hns3_clean_rx_ring(
                        hns3_nic_alloc_rx_buffers(ring,
                                                  clean_count + unused_count);
                        clean_count = 0;
-                       unused_count = hns3_desc_unused(ring);
+                       unused_count = hns3_desc_unused(ring) -
+                                       ring->pending_buf;
                }
 
                /* Poll one pkt */
-               err = hns3_handle_rx_bd(ring, &skb, &bnum);
+               err = hns3_handle_rx_bd(ring, &skb);
                if (unlikely(!skb)) /* This fault cannot be repaired */
                        goto out;
 
-               recv_bds += bnum;
-               clean_count += bnum;
-               if (unlikely(err)) {  /* Do jump the err */
-                       recv_pkts++;
+               if (err == -ENXIO) { /* Do not get FE for the packet */
+                       goto out;
+               } else if (unlikely(err)) {  /* Do jump the err */
+                       recv_bds += ring->pending_buf;
+                       clean_count += ring->pending_buf;
+                       ring->skb = NULL;
+                       ring->pending_buf = 0;
                        continue;
                }
 
                /* Do update ip stack process */
                skb->protocol = eth_type_trans(skb, netdev);
                rx_fn(ring, skb);
+               recv_bds += ring->pending_buf;
+               clean_count += ring->pending_buf;
+               ring->skb = NULL;
+               ring->pending_buf = 0;
 
                recv_pkts++;
        }
@@ -2669,6 +2871,7 @@ static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector)
 
 static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
 {
+       struct hns3_nic_priv *priv = netdev_priv(napi->dev);
        struct hns3_enet_ring *ring;
        int rx_pkt_total = 0;
 
@@ -2677,6 +2880,11 @@ static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
        bool clean_complete = true;
        int rx_budget;
 
+       if (unlikely(test_bit(HNS3_NIC_STATE_DOWN, &priv->state))) {
+               napi_complete(napi);
+               return 0;
+       }
+
        /* Since the actual Tx work is minimal, we can give the Tx a larger
         * budget and be more aggressive about cleaning up the Tx descriptors.
         */
@@ -2701,9 +2909,11 @@ static int hns3_nic_common_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       napi_complete(napi);
-       hns3_update_new_int_gl(tqp_vector);
-       hns3_mask_vector_irq(tqp_vector, 1);
+       if (likely(!test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) &&
+           napi_complete(napi)) {
+               hns3_update_new_int_gl(tqp_vector);
+               hns3_mask_vector_irq(tqp_vector, 1);
+       }
 
        return rx_pkt_total;
 }
@@ -3319,6 +3529,22 @@ static void hns3_nic_set_priv_ops(struct net_device *netdev)
                priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx;
 }
 
+static int hns3_client_start(struct hnae3_handle *handle)
+{
+       if (!handle->ae_algo->ops->client_start)
+               return 0;
+
+       return handle->ae_algo->ops->client_start(handle);
+}
+
+static void hns3_client_stop(struct hnae3_handle *handle)
+{
+       if (!handle->ae_algo->ops->client_stop)
+               return;
+
+       handle->ae_algo->ops->client_stop(handle);
+}
+
 static int hns3_client_init(struct hnae3_handle *handle)
 {
        struct pci_dev *pdev = handle->pdev;
@@ -3337,7 +3563,6 @@ static int hns3_client_init(struct hnae3_handle *handle)
        priv->dev = &pdev->dev;
        priv->netdev = netdev;
        priv->ae_handle = handle;
-       priv->ae_handle->last_reset_time = jiffies;
        priv->tx_timeout_count = 0;
 
        handle->kinfo.netdev = netdev;
@@ -3357,11 +3582,6 @@ static int hns3_client_init(struct hnae3_handle *handle)
        /* Carrier off reporting is important to ethtool even BEFORE open */
        netif_carrier_off(netdev);
 
-       if (handle->flags & HNAE3_SUPPORT_VF)
-               handle->reset_level = HNAE3_VF_RESET;
-       else
-               handle->reset_level = HNAE3_FUNC_RESET;
-
        ret = hns3_get_ring_config(priv);
        if (ret) {
                ret = -ENOMEM;
@@ -3392,10 +3612,20 @@ static int hns3_client_init(struct hnae3_handle *handle)
                goto out_reg_netdev_fail;
        }
 
+       ret = hns3_client_start(handle);
+       if (ret) {
+               dev_err(priv->dev, "hns3_client_start fail! ret=%d\n", ret);
+                       goto out_reg_netdev_fail;
+       }
+
        hns3_dcbnl_setup(handle);
 
-       /* MTU range: (ETH_MIN_MTU(kernel default) - 9706) */
-       netdev->max_mtu = HNS3_MAX_MTU - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
+       hns3_dbg_init(handle);
+
+       /* MTU range: (ETH_MIN_MTU(kernel default) - 9702) */
+       netdev->max_mtu = HNS3_MAX_MTU;
+
+       set_bit(HNS3_NIC_STATE_INITED, &priv->state);
 
        return ret;
 
@@ -3418,11 +3648,18 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
        struct hns3_nic_priv *priv = netdev_priv(netdev);
        int ret;
 
+       hns3_client_stop(handle);
+
        hns3_remove_hw_addr(netdev);
 
        if (netdev->reg_state != NETREG_UNINITIALIZED)
                unregister_netdev(netdev);
 
+       if (!test_and_clear_bit(HNS3_NIC_STATE_INITED, &priv->state)) {
+               netdev_warn(netdev, "already uninitialized\n");
+               goto out_netdev_free;
+       }
+
        hns3_del_all_fd_rules(netdev, true);
 
        hns3_force_clear_all_rx_ring(handle);
@@ -3441,8 +3678,11 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
 
        hns3_put_ring_config(priv);
 
+       hns3_dbg_uninit(handle);
+
        priv->ring_data = NULL;
 
+out_netdev_free:
        free_netdev(netdev);
 }
 
@@ -3708,8 +3948,22 @@ static void hns3_restore_coal(struct hns3_nic_priv *priv)
 
 static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
 {
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
        struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct net_device *ndev = kinfo->netdev;
+       struct hns3_nic_priv *priv = netdev_priv(ndev);
+
+       if (test_and_set_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
+               return 0;
+
+       /* it is cumbersome for hardware to pick-and-choose entries for deletion
+        * from table space. Hence, for function reset software intervention is
+        * required to delete the entries
+        */
+       if (hns3_dev_ongoing_func_reset(ae_dev)) {
+               hns3_remove_hw_addr(ndev);
+               hns3_del_all_fd_rules(ndev, false);
+       }
 
        if (!netif_running(ndev))
                return 0;
@@ -3720,6 +3974,7 @@ static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
 static int hns3_reset_notify_up_enet(struct hnae3_handle *handle)
 {
        struct hnae3_knic_private_info *kinfo = &handle->kinfo;
+       struct hns3_nic_priv *priv = netdev_priv(kinfo->netdev);
        int ret = 0;
 
        if (netif_running(kinfo->netdev)) {
@@ -3729,9 +3984,10 @@ static int hns3_reset_notify_up_enet(struct hnae3_handle *handle)
                                   "hns net up fail, ret=%d!\n", ret);
                        return ret;
                }
-               handle->last_reset_time = jiffies;
        }
 
+       clear_bit(HNS3_NIC_STATE_RESETTING, &priv->state);
+
        return ret;
 }
 
@@ -3771,28 +4027,44 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle)
        /* Carrier off reporting is important to ethtool even BEFORE open */
        netif_carrier_off(netdev);
 
+       ret = hns3_nic_alloc_vector_data(priv);
+       if (ret)
+               return ret;
+
        hns3_restore_coal(priv);
 
        ret = hns3_nic_init_vector_data(priv);
        if (ret)
-               return ret;
+               goto err_dealloc_vector;
 
        ret = hns3_init_all_ring(priv);
-       if (ret) {
-               hns3_nic_uninit_vector_data(priv);
-               priv->ring_data = NULL;
-       }
+       if (ret)
+               goto err_uninit_vector;
+
+       set_bit(HNS3_NIC_STATE_INITED, &priv->state);
+
+       return ret;
+
+err_uninit_vector:
+       hns3_nic_uninit_vector_data(priv);
+       priv->ring_data = NULL;
+err_dealloc_vector:
+       hns3_nic_dealloc_vector_data(priv);
 
        return ret;
 }
 
 static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
 {
-       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
        struct net_device *netdev = handle->kinfo.netdev;
        struct hns3_nic_priv *priv = netdev_priv(netdev);
        int ret;
 
+       if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state)) {
+               netdev_warn(netdev, "already uninitialized\n");
+               return 0;
+       }
+
        hns3_force_clear_all_rx_ring(handle);
 
        ret = hns3_nic_uninit_vector_data(priv);
@@ -3803,18 +4075,15 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
 
        hns3_store_coal(priv);
 
+       ret = hns3_nic_dealloc_vector_data(priv);
+       if (ret)
+               netdev_err(netdev, "dealloc vector error\n");
+
        ret = hns3_uninit_all_ring(priv);
        if (ret)
                netdev_err(netdev, "uninit ring error\n");
 
-       /* it is cumbersome for hardware to pick-and-choose entries for deletion
-        * from table space. Hence, for function reset software intervention is
-        * required to delete the entries
-        */
-       if (hns3_dev_ongoing_func_reset(ae_dev)) {
-               hns3_remove_hw_addr(netdev);
-               hns3_del_all_fd_rules(netdev, false);
-       }
+       clear_bit(HNS3_NIC_STATE_INITED, &priv->state);
 
        return ret;
 }
@@ -3980,15 +4249,23 @@ static int __init hns3_init_module(void)
 
        INIT_LIST_HEAD(&client.node);
 
+       hns3_dbg_register_debugfs(hns3_driver_name);
+
        ret = hnae3_register_client(&client);
        if (ret)
-               return ret;
+               goto err_reg_client;
 
        ret = pci_register_driver(&hns3_driver);
        if (ret)
-               hnae3_unregister_client(&client);
+               goto err_reg_driver;
 
        return ret;
+
+err_reg_driver:
+       hnae3_unregister_client(&client);
+err_reg_client:
+       hns3_dbg_unregister_debugfs();
+       return ret;
 }
 module_init(hns3_init_module);
 
@@ -4000,6 +4277,7 @@ static void __exit hns3_exit_module(void)
 {
        pci_unregister_driver(&hns3_driver);
        hnae3_unregister_client(&client);
+       hns3_dbg_unregister_debugfs();
 }
 module_exit(hns3_exit_module);
 
index d3636d0..4e4b48b 100644 (file)
@@ -15,7 +15,7 @@ extern const char hns3_driver_version[];
 enum hns3_nic_state {
        HNS3_NIC_STATE_TESTING,
        HNS3_NIC_STATE_RESETTING,
-       HNS3_NIC_STATE_REINITING,
+       HNS3_NIC_STATE_INITED,
        HNS3_NIC_STATE_DOWN,
        HNS3_NIC_STATE_DISABLED,
        HNS3_NIC_STATE_REMOVING,
@@ -47,7 +47,7 @@ enum hns3_nic_state {
 #define HNS3_RING_PREFETCH_EN_REG              0x0007C
 #define HNS3_RING_CFG_VF_NUM_REG               0x00080
 #define HNS3_RING_ASID_REG                     0x0008C
-#define HNS3_RING_RX_VM_REG                    0x00090
+#define HNS3_RING_EN_REG                       0x00090
 #define HNS3_RING_T0_BE_RST                    0x00094
 #define HNS3_RING_COULD_BE_RST                 0x00098
 #define HNS3_RING_WRR_WEIGHT_REG               0x0009c
@@ -76,7 +76,10 @@ enum hns3_nic_state {
 #define HNS3_RING_MAX_PENDING                  32768
 #define HNS3_RING_MIN_PENDING                  8
 #define HNS3_RING_BD_MULTIPLE                  8
-#define HNS3_MAX_MTU                           9728
+/* max frame size of mac */
+#define HNS3_MAC_MAX_FRAME                     9728
+#define HNS3_MAX_MTU \
+       (HNS3_MAC_MAX_FRAME - (ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN))
 
 #define HNS3_BD_SIZE_512_TYPE                  0
 #define HNS3_BD_SIZE_1024_TYPE                 1
@@ -109,6 +112,10 @@ enum hns3_nic_state {
 #define HNS3_RXD_DOI_B                         21
 #define HNS3_RXD_OL3E_B                                22
 #define HNS3_RXD_OL4E_B                                23
+#define HNS3_RXD_GRO_COUNT_S                   24
+#define HNS3_RXD_GRO_COUNT_M                   (0x3f << HNS3_RXD_GRO_COUNT_S)
+#define HNS3_RXD_GRO_FIXID_B                   30
+#define HNS3_RXD_GRO_ECN_B                     31
 
 #define HNS3_RXD_ODMAC_S                       0
 #define HNS3_RXD_ODMAC_M                       (0x3 << HNS3_RXD_ODMAC_S)
@@ -135,9 +142,8 @@ enum hns3_nic_state {
 #define HNS3_RXD_TSIND_S                       12
 #define HNS3_RXD_TSIND_M                       (0x7 << HNS3_RXD_TSIND_S)
 #define HNS3_RXD_LKBK_B                                15
-#define HNS3_RXD_HDL_S                         16
-#define HNS3_RXD_HDL_M                         (0x7ff << HNS3_RXD_HDL_S)
-#define HNS3_RXD_HSIND_B                       31
+#define HNS3_RXD_GRO_SIZE_S                    16
+#define HNS3_RXD_GRO_SIZE_M                    (0x3ff << HNS3_RXD_GRO_SIZE_S)
 
 #define HNS3_TXD_L3T_S                         0
 #define HNS3_TXD_L3T_M                         (0x3 << HNS3_TXD_L3T_S)
@@ -194,6 +200,8 @@ enum hns3_nic_state {
 #define HNS3_VECTOR_RL_OFFSET                  0x900
 #define HNS3_VECTOR_RL_EN_B                    6
 
+#define HNS3_RING_EN_B                         0
+
 enum hns3_pkt_l3t_type {
        HNS3_L3T_NONE,
        HNS3_L3T_IPV6,
@@ -399,11 +407,19 @@ struct hns3_enet_ring {
         */
        int next_to_clean;
 
+       int pull_len; /* head length for current packet */
+       u32 frag_num;
+       unsigned char *va; /* first buffer address for current packet */
+
        u32 flag;          /* ring attribute */
        int irq_init_flag;
 
        int numa_node;
        cpumask_t affinity_mask;
+
+       int pending_buf;
+       struct sk_buff *skb;
+       struct sk_buff *tail_skb;
 };
 
 struct hns_queue;
@@ -577,6 +593,11 @@ static inline int is_ring_empty(struct hns3_enet_ring *ring)
        return ring->next_to_use == ring->next_to_clean;
 }
 
+static inline u32 hns3_read_reg(void __iomem *base, u32 reg)
+{
+       return readl(base + reg);
+}
+
 static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value)
 {
        u8 __iomem *reg_addr = READ_ONCE(base);
@@ -586,7 +607,21 @@ static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value)
 
 static inline bool hns3_dev_ongoing_func_reset(struct hnae3_ae_dev *ae_dev)
 {
-       return (ae_dev && (ae_dev->reset_type == HNAE3_FUNC_RESET));
+       return (ae_dev && (ae_dev->reset_type == HNAE3_FUNC_RESET ||
+                          ae_dev->reset_type == HNAE3_FLR_RESET ||
+                          ae_dev->reset_type == HNAE3_VF_FUNC_RESET ||
+                          ae_dev->reset_type == HNAE3_VF_FULL_RESET ||
+                          ae_dev->reset_type == HNAE3_VF_PF_FUNC_RESET));
+}
+
+#define hns3_read_dev(a, reg) \
+       hns3_read_reg((a)->io_base, (reg))
+
+static inline bool hns3_nic_resetting(struct net_device *netdev)
+{
+       struct hns3_nic_priv *priv = netdev_priv(netdev);
+
+       return test_bit(HNS3_NIC_STATE_RESETTING, &priv->state);
 }
 
 #define hns3_write_dev(a, reg, value) \
@@ -648,4 +683,8 @@ void hns3_dcbnl_setup(struct hnae3_handle *handle);
 static inline void hns3_dcbnl_setup(struct hnae3_handle *handle) {}
 #endif
 
+void hns3_dbg_init(struct hnae3_handle *handle);
+void hns3_dbg_uninit(struct hnae3_handle *handle);
+void hns3_dbg_register_debugfs(const char *debugfs_dir_name);
+void hns3_dbg_unregister_debugfs(void);
 #endif
index a4762c2..e678b69 100644 (file)
@@ -291,6 +291,11 @@ static void hns3_self_test(struct net_device *ndev,
        int test_index = 0;
        u32 i;
 
+       if (hns3_nic_resetting(ndev)) {
+               netdev_err(ndev, "dev resetting!");
+               return;
+       }
+
        /* Only do offline selftest, or pass by default */
        if (eth_test->flags != ETH_TEST_FL_OFFLINE)
                return;
@@ -530,6 +535,11 @@ static void hns3_get_ringparam(struct net_device *netdev,
        struct hnae3_handle *h = priv->ae_handle;
        int queue_num = h->kinfo.num_tqps;
 
+       if (hns3_nic_resetting(netdev)) {
+               netdev_err(netdev, "dev resetting!");
+               return;
+       }
+
        param->tx_max_pending = HNS3_RING_MAX_PENDING;
        param->rx_max_pending = HNS3_RING_MAX_PENDING;
 
@@ -760,6 +770,9 @@ static int hns3_set_ringparam(struct net_device *ndev,
        u32 old_desc_num, new_desc_num;
        int ret;
 
+       if (hns3_nic_resetting(ndev))
+               return -EBUSY;
+
        if (param->rx_mini_pending || param->rx_jumbo_pending)
                return -EINVAL;
 
@@ -808,7 +821,7 @@ static int hns3_set_ringparam(struct net_device *ndev,
        }
 
        if (if_running)
-               ret = dev_open(ndev);
+               ret = dev_open(ndev, NULL);
 
        return ret;
 }
@@ -872,6 +885,9 @@ static int hns3_get_coalesce_per_queue(struct net_device *netdev, u32 queue,
        struct hnae3_handle *h = priv->ae_handle;
        u16 queue_num = h->kinfo.num_tqps;
 
+       if (hns3_nic_resetting(netdev))
+               return -EBUSY;
+
        if (queue >= queue_num) {
                netdev_err(netdev,
                           "Invalid queue value %d! Queue max id=%d\n",
@@ -1033,6 +1049,9 @@ static int hns3_set_coalesce(struct net_device *netdev,
        int ret;
        int i;
 
+       if (hns3_nic_resetting(netdev))
+               return -EBUSY;
+
        ret = hns3_check_coalesce_para(netdev, cmd);
        if (ret)
                return ret;
index 580e817..fffe8c1 100644 (file)
@@ -6,6 +6,6 @@
 ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3
 
 obj-$(CONFIG_HNS3_HCLGE) += hclge.o
-hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o hclge_err.o
+hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o hclge_err.o  hclge_debugfs.o
 
 hclge-$(CONFIG_HNS3_DCB) += hclge_dcb.o
index 690f62e..8af0cef 100644 (file)
@@ -350,11 +350,20 @@ int hclge_cmd_init(struct hclge_dev *hdev)
        hdev->hw.cmq.crq.next_to_use = 0;
 
        hclge_cmd_init_regs(&hdev->hw);
-       clear_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
 
        spin_unlock_bh(&hdev->hw.cmq.crq.lock);
        spin_unlock_bh(&hdev->hw.cmq.csq.lock);
 
+       clear_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
+
+       /* Check if there is new reset pending, because the higher level
+        * reset may happen when lower level reset is being processed.
+        */
+       if ((hclge_is_reset_pending(hdev))) {
+               set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
+               return -EBUSY;
+       }
+
        ret = hclge_cmd_query_firmware_version(&hdev->hw, &version);
        if (ret) {
                dev_err(&hdev->pdev->dev,
index 872cd4b..b1ee6fe 100644 (file)
@@ -90,7 +90,6 @@ enum hclge_opcode_type {
        /* MAC command */
        HCLGE_OPC_CONFIG_MAC_MODE       = 0x0301,
        HCLGE_OPC_CONFIG_AN_MODE        = 0x0304,
-       HCLGE_OPC_QUERY_AN_RESULT       = 0x0306,
        HCLGE_OPC_QUERY_LINK_STATUS     = 0x0307,
        HCLGE_OPC_CONFIG_MAX_FRM_SIZE   = 0x0308,
        HCLGE_OPC_CONFIG_SPEED_DUP      = 0x0309,
@@ -126,6 +125,7 @@ enum hclge_opcode_type {
        HCLGE_OPC_TM_PRI_SCH_MODE_CFG   = 0x0813,
        HCLGE_OPC_TM_QS_SCH_MODE_CFG    = 0x0814,
        HCLGE_OPC_TM_BP_TO_QSET_MAPPING = 0x0815,
+       HCLGE_OPC_ETS_TC_WEIGHT         = 0x0843,
 
        /* Packet buffer allocate commands */
        HCLGE_OPC_TX_BUFF_ALLOC         = 0x0901,
@@ -152,6 +152,7 @@ enum hclge_opcode_type {
 
        /* TSO command */
        HCLGE_OPC_TSO_GENERIC_CONFIG    = 0x0C01,
+       HCLGE_OPC_GRO_GENERIC_CONFIG    = 0x0C10,
 
        /* RSS commands */
        HCLGE_OPC_RSS_GENERIC_CONFIG    = 0x0D01,
@@ -210,27 +211,33 @@ enum hclge_opcode_type {
        /* Led command */
        HCLGE_OPC_LED_STATUS_CFG        = 0xB000,
 
+       /* SFP command */
+       HCLGE_OPC_SFP_GET_SPEED         = 0x7104,
+
        /* Error INT commands */
+       HCLGE_MAC_COMMON_INT_EN         = 0x030E,
        HCLGE_TM_SCH_ECC_INT_EN         = 0x0829,
-       HCLGE_TM_SCH_ECC_ERR_RINT_CMD   = 0x082d,
-       HCLGE_TM_SCH_ECC_ERR_RINT_CE    = 0x082f,
-       HCLGE_TM_SCH_ECC_ERR_RINT_NFE   = 0x0830,
-       HCLGE_TM_SCH_ECC_ERR_RINT_FE    = 0x0831,
-       HCLGE_TM_SCH_MBIT_ECC_INFO_CMD  = 0x0833,
+       HCLGE_SSU_ECC_INT_CMD           = 0x0989,
+       HCLGE_SSU_COMMON_INT_CMD        = 0x098C,
+       HCLGE_PPU_MPF_ECC_INT_CMD       = 0x0B40,
+       HCLGE_PPU_MPF_OTHER_INT_CMD     = 0x0B41,
+       HCLGE_PPU_PF_OTHER_INT_CMD      = 0x0B42,
        HCLGE_COMMON_ECC_INT_CFG        = 0x1505,
-       HCLGE_IGU_EGU_TNL_INT_QUERY     = 0x1802,
+       HCLGE_QUERY_RAS_INT_STS_BD_NUM  = 0x1510,
+       HCLGE_QUERY_CLEAR_MPF_RAS_INT   = 0x1511,
+       HCLGE_QUERY_CLEAR_PF_RAS_INT    = 0x1512,
+       HCLGE_QUERY_MSIX_INT_STS_BD_NUM = 0x1513,
+       HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT      = 0x1514,
+       HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT       = 0x1515,
+       HCLGE_CONFIG_ROCEE_RAS_INT_EN   = 0x1580,
+       HCLGE_QUERY_CLEAR_ROCEE_RAS_INT = 0x1581,
+       HCLGE_ROCEE_PF_RAS_INT_CMD      = 0x1584,
        HCLGE_IGU_EGU_TNL_INT_EN        = 0x1803,
-       HCLGE_IGU_EGU_TNL_INT_CLR       = 0x1804,
-       HCLGE_IGU_COMMON_INT_QUERY      = 0x1805,
        HCLGE_IGU_COMMON_INT_EN         = 0x1806,
-       HCLGE_IGU_COMMON_INT_CLR        = 0x1807,
        HCLGE_TM_QCN_MEM_INT_CFG        = 0x1A14,
-       HCLGE_TM_QCN_MEM_INT_INFO_CMD   = 0x1A17,
        HCLGE_PPP_CMD0_INT_CMD          = 0x2100,
        HCLGE_PPP_CMD1_INT_CMD          = 0x2101,
-       HCLGE_NCSI_INT_QUERY            = 0x2400,
        HCLGE_NCSI_INT_EN               = 0x2401,
-       HCLGE_NCSI_INT_CLR              = 0x2402,
 };
 
 #define HCLGE_TQP_REG_OFFSET           0x80000
@@ -542,20 +549,6 @@ struct hclge_config_mac_speed_dup_cmd {
        u8 rsv[22];
 };
 
-#define HCLGE_QUERY_SPEED_S            3
-#define HCLGE_QUERY_AN_B               0
-#define HCLGE_QUERY_DUPLEX_B           2
-
-#define HCLGE_QUERY_SPEED_M            GENMASK(4, 0)
-#define HCLGE_QUERY_AN_M               BIT(HCLGE_QUERY_AN_B)
-#define HCLGE_QUERY_DUPLEX_M           BIT(HCLGE_QUERY_DUPLEX_B)
-
-struct hclge_query_an_speed_dup_cmd {
-       u8 an_syn_dup_speed;
-       u8 pause;
-       u8 rsv[23];
-};
-
 #define HCLGE_RING_ID_MASK             GENMASK(9, 0)
 #define HCLGE_TQP_ENABLE_B             0
 
@@ -572,6 +565,11 @@ struct hclge_config_auto_neg_cmd {
        u8      rsv[20];
 };
 
+struct hclge_sfp_speed_cmd {
+       __le32  sfp_speed;
+       u32     rsv[5];
+};
+
 #define HCLGE_MAC_UPLINK_PORT          0x100
 
 struct hclge_config_max_frm_size_cmd {
@@ -758,6 +756,12 @@ struct hclge_cfg_tso_status_cmd {
        u8 rsv[20];
 };
 
+#define HCLGE_GRO_EN_B         0
+struct hclge_cfg_gro_status_cmd {
+       __le16 gro_en;
+       u8 rsv[22];
+};
+
 #define HCLGE_TSO_MSS_MIN      256
 #define HCLGE_TSO_MSS_MAX      9668
 
index e72f724..f6323b2 100644 (file)
@@ -35,7 +35,9 @@ static int hclge_ieee_ets_to_tm_info(struct hclge_dev *hdev,
                }
        }
 
-       return hclge_tm_prio_tc_info_update(hdev, ets->prio_tc);
+       hclge_tm_prio_tc_info_update(hdev, ets->prio_tc);
+
+       return 0;
 }
 
 static void hclge_tm_info_to_ieee_ets(struct hclge_dev *hdev,
@@ -70,25 +72,61 @@ static int hclge_ieee_getets(struct hnae3_handle *h, struct ieee_ets *ets)
        return 0;
 }
 
+static int hclge_dcb_common_validate(struct hclge_dev *hdev, u8 num_tc,
+                                    u8 *prio_tc)
+{
+       int i;
+
+       if (num_tc > hdev->tc_max) {
+               dev_err(&hdev->pdev->dev,
+                       "tc num checking failed, %u > tc_max(%u)\n",
+                       num_tc, hdev->tc_max);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
+               if (prio_tc[i] >= num_tc) {
+                       dev_err(&hdev->pdev->dev,
+                               "prio_tc[%u] checking failed, %u >= num_tc(%u)\n",
+                               i, prio_tc[i], num_tc);
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < hdev->num_alloc_vport; i++) {
+               if (num_tc > hdev->vport[i].alloc_tqps) {
+                       dev_err(&hdev->pdev->dev,
+                               "allocated tqp(%u) checking failed, %u > tqp(%u)\n",
+                               i, num_tc, hdev->vport[i].alloc_tqps);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int hclge_ets_validate(struct hclge_dev *hdev, struct ieee_ets *ets,
                              u8 *tc, bool *changed)
 {
        bool has_ets_tc = false;
        u32 total_ets_bw = 0;
        u8 max_tc = 0;
+       int ret;
        u8 i;
 
-       for (i = 0; i < HNAE3_MAX_TC; i++) {
-               if (ets->prio_tc[i] >= hdev->tc_max ||
-                   i >= hdev->tc_max)
-                       return -EINVAL;
-
+       for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
                if (ets->prio_tc[i] != hdev->tm_info.prio_tc[i])
                        *changed = true;
 
                if (ets->prio_tc[i] > max_tc)
                        max_tc = ets->prio_tc[i];
+       }
 
+       ret = hclge_dcb_common_validate(hdev, max_tc + 1, ets->prio_tc);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < HNAE3_MAX_TC; i++) {
                switch (ets->tc_tsa[i]) {
                case IEEE_8021QAZ_TSA_STRICT:
                        if (hdev->tm_info.tc_info[i].tc_sch_mode !=
@@ -184,9 +222,7 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets)
        if (ret)
                return ret;
 
-       ret = hclge_tm_schd_info_update(hdev, num_tc);
-       if (ret)
-               return ret;
+       hclge_tm_schd_info_update(hdev, num_tc);
 
        ret = hclge_ieee_ets_to_tm_info(hdev, ets);
        if (ret)
@@ -305,20 +341,12 @@ static int hclge_setup_tc(struct hnae3_handle *h, u8 tc, u8 *prio_tc)
        if (hdev->flag & HCLGE_FLAG_DCB_ENABLE)
                return -EINVAL;
 
-       if (tc > hdev->tc_max) {
-               dev_err(&hdev->pdev->dev,
-                       "setup tc failed, tc(%u) > tc_max(%u)\n",
-                       tc, hdev->tc_max);
-               return -EINVAL;
-       }
-
-       ret = hclge_tm_schd_info_update(hdev, tc);
+       ret = hclge_dcb_common_validate(hdev, tc, prio_tc);
        if (ret)
-               return ret;
+               return -EINVAL;
 
-       ret = hclge_tm_prio_tc_info_update(hdev, prio_tc);
-       if (ret)
-               return ret;
+       hclge_tm_schd_info_update(hdev, tc);
+       hclge_tm_prio_tc_info_update(hdev, prio_tc);
 
        ret = hclge_tm_init_hw(hdev);
        if (ret)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
new file mode 100644 (file)
index 0000000..14577bb
--- /dev/null
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#include <linux/device.h>
+
+#include "hclge_debugfs.h"
+#include "hclge_cmd.h"
+#include "hclge_main.h"
+#include "hclge_tm.h"
+#include "hnae3.h"
+
+static void hclge_title_idx_print(struct hclge_dev *hdev, bool flag, int index,
+                                 char *title_buf, char *true_buf,
+                                 char *false_buf)
+{
+       if (flag)
+               dev_info(&hdev->pdev->dev, "%s(%d): %s\n", title_buf, index,
+                        true_buf);
+       else
+               dev_info(&hdev->pdev->dev, "%s(%d): %s\n", title_buf, index,
+                        false_buf);
+}
+
+static void hclge_dbg_dump_tc(struct hclge_dev *hdev)
+{
+       struct hclge_ets_tc_weight_cmd *ets_weight;
+       struct hclge_desc desc;
+       int i, ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_ETS_TC_WEIGHT, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "dump tc fail, status is %d.\n", ret);
+               return;
+       }
+
+       ets_weight = (struct hclge_ets_tc_weight_cmd *)desc.data;
+
+       dev_info(&hdev->pdev->dev, "dump tc\n");
+       dev_info(&hdev->pdev->dev, "weight_offset: %u\n",
+                ets_weight->weight_offset);
+
+       for (i = 0; i < HNAE3_MAX_TC; i++)
+               hclge_title_idx_print(hdev, ets_weight->tc_weight[i], i,
+                                     "tc", "no sp mode", "sp mode");
+}
+
+static void hclge_dbg_dump_tm_pg(struct hclge_dev *hdev)
+{
+       struct hclge_port_shapping_cmd *port_shap_cfg_cmd;
+       struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd;
+       struct hclge_pg_shapping_cmd *pg_shap_cfg_cmd;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       cmd = HCLGE_OPC_TM_PG_C_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       pg_shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG_C pg_id: %u\n", pg_shap_cfg_cmd->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_C pg_shapping: 0x%x\n",
+                pg_shap_cfg_cmd->pg_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PG_P_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       pg_shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG_P pg_id: %u\n", pg_shap_cfg_cmd->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_P pg_shapping: 0x%x\n",
+                pg_shap_cfg_cmd->pg_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PORT_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       port_shap_cfg_cmd = (struct hclge_port_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PORT port_shapping: 0x%x\n",
+                port_shap_cfg_cmd->port_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PG_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "PG_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_PRI_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "PRI_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_QS_SCH_MODE_CFG;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "QS_SCH pg_id: %u\n", desc.data[0]);
+
+       cmd = HCLGE_OPC_TM_BP_TO_QSET_MAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_pg_cmd_send;
+
+       bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET pg_id: %u\n",
+                bp_to_qs_map_cmd->tc_id);
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET pg_shapping: 0x%x\n",
+                bp_to_qs_map_cmd->qs_group_id);
+       dev_info(&hdev->pdev->dev, "BP_TO_QSET qs_bit_map: 0x%x\n",
+                bp_to_qs_map_cmd->qs_bit_map);
+       return;
+
+err_tm_pg_cmd_send:
+       dev_err(&hdev->pdev->dev, "dump tm_pg fail(0x%x), status is %d\n",
+               cmd, ret);
+}
+
+static void hclge_dbg_dump_tm(struct hclge_dev *hdev)
+{
+       struct hclge_priority_weight_cmd *priority_weight;
+       struct hclge_pg_to_pri_link_cmd *pg_to_pri_map;
+       struct hclge_qs_to_pri_link_cmd *qs_to_pri_map;
+       struct hclge_nq_to_qs_link_cmd *nq_to_qs_map;
+       struct hclge_pri_shapping_cmd *shap_cfg_cmd;
+       struct hclge_pg_weight_cmd *pg_weight;
+       struct hclge_qs_weight_cmd *qs_weight;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc;
+       int ret;
+
+       cmd = HCLGE_OPC_TM_PG_TO_PRI_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       pg_to_pri_map = (struct hclge_pg_to_pri_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump tm\n");
+       dev_info(&hdev->pdev->dev, "PG_TO_PRI gp_id: %u\n",
+                pg_to_pri_map->pg_id);
+       dev_info(&hdev->pdev->dev, "PG_TO_PRI map: 0x%x\n",
+                pg_to_pri_map->pri_bit_map);
+
+       cmd = HCLGE_OPC_TM_QS_TO_PRI_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       qs_to_pri_map = (struct hclge_qs_to_pri_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI qs_id: %u\n",
+                qs_to_pri_map->qs_id);
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI priority: %u\n",
+                qs_to_pri_map->priority);
+       dev_info(&hdev->pdev->dev, "QS_TO_PRI link_vld: %u\n",
+                qs_to_pri_map->link_vld);
+
+       cmd = HCLGE_OPC_TM_NQ_TO_QS_LINK;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       nq_to_qs_map = (struct hclge_nq_to_qs_link_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "NQ_TO_QS nq_id: %u\n", nq_to_qs_map->nq_id);
+       dev_info(&hdev->pdev->dev, "NQ_TO_QS qset_id: %u\n",
+                nq_to_qs_map->qset_id);
+
+       cmd = HCLGE_OPC_TM_PG_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       pg_weight = (struct hclge_pg_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PG pg_id: %u\n", pg_weight->pg_id);
+       dev_info(&hdev->pdev->dev, "PG dwrr: %u\n", pg_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_QS_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       qs_weight = (struct hclge_qs_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "QS qs_id: %u\n", qs_weight->qs_id);
+       dev_info(&hdev->pdev->dev, "QS dwrr: %u\n", qs_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_PRI_WEIGHT;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       priority_weight = (struct hclge_priority_weight_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI pri_id: %u\n", priority_weight->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI dwrr: %u\n", priority_weight->dwrr);
+
+       cmd = HCLGE_OPC_TM_PRI_C_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI_C pri_id: %u\n", shap_cfg_cmd->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI_C pri_shapping: 0x%x\n",
+                shap_cfg_cmd->pri_shapping_para);
+
+       cmd = HCLGE_OPC_TM_PRI_P_SHAPPING;
+       hclge_cmd_setup_basic_desc(&desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               goto err_tm_cmd_send;
+
+       shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "PRI_P pri_id: %u\n", shap_cfg_cmd->pri_id);
+       dev_info(&hdev->pdev->dev, "PRI_P pri_shapping: 0x%x\n",
+                shap_cfg_cmd->pri_shapping_para);
+
+       hclge_dbg_dump_tm_pg(hdev);
+
+       return;
+
+err_tm_cmd_send:
+       dev_err(&hdev->pdev->dev, "dump tm fail(0x%x), status is %d\n",
+               cmd, ret);
+}
+
+static void hclge_dbg_dump_qos_pause_cfg(struct hclge_dev *hdev)
+{
+       struct hclge_cfg_pause_param_cmd *pause_param;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_MAC_PARA, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "dump checksum fail, status is %d.\n",
+                       ret);
+               return;
+       }
+
+       pause_param = (struct hclge_cfg_pause_param_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump qos pause cfg\n");
+       dev_info(&hdev->pdev->dev, "pause_trans_gap: 0x%x\n",
+                pause_param->pause_trans_gap);
+       dev_info(&hdev->pdev->dev, "pause_trans_time: 0x%x\n",
+                pause_param->pause_trans_time);
+}
+
+static void hclge_dbg_dump_qos_pri_map(struct hclge_dev *hdev)
+{
+       struct hclge_qos_pri_map_cmd *pri_map;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PRI_TO_TC_MAPPING, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "dump qos pri map fail, status is %d.\n", ret);
+               return;
+       }
+
+       pri_map = (struct hclge_qos_pri_map_cmd *)desc.data;
+       dev_info(&hdev->pdev->dev, "dump qos pri map\n");
+       dev_info(&hdev->pdev->dev, "vlan_to_pri: 0x%x\n", pri_map->vlan_pri);
+       dev_info(&hdev->pdev->dev, "pri_0_to_tc: 0x%x\n", pri_map->pri0_tc);
+       dev_info(&hdev->pdev->dev, "pri_1_to_tc: 0x%x\n", pri_map->pri1_tc);
+       dev_info(&hdev->pdev->dev, "pri_2_to_tc: 0x%x\n", pri_map->pri2_tc);
+       dev_info(&hdev->pdev->dev, "pri_3_to_tc: 0x%x\n", pri_map->pri3_tc);
+       dev_info(&hdev->pdev->dev, "pri_4_to_tc: 0x%x\n", pri_map->pri4_tc);
+       dev_info(&hdev->pdev->dev, "pri_5_to_tc: 0x%x\n", pri_map->pri5_tc);
+       dev_info(&hdev->pdev->dev, "pri_6_to_tc: 0x%x\n", pri_map->pri6_tc);
+       dev_info(&hdev->pdev->dev, "pri_7_to_tc: 0x%x\n", pri_map->pri7_tc);
+}
+
+static void hclge_dbg_dump_qos_buf_cfg(struct hclge_dev *hdev)
+{
+       struct hclge_tx_buff_alloc_cmd *tx_buf_cmd;
+       struct hclge_rx_priv_buff_cmd *rx_buf_cmd;
+       struct hclge_rx_priv_wl_buf *rx_priv_wl;
+       struct hclge_rx_com_wl *rx_packet_cnt;
+       struct hclge_rx_com_thrd *rx_com_thrd;
+       struct hclge_rx_com_wl *rx_com_wl;
+       enum hclge_opcode_type cmd;
+       struct hclge_desc desc[2];
+       int i, ret;
+
+       cmd = HCLGE_OPC_TX_BUFF_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "dump qos buf cfg\n");
+
+       tx_buf_cmd = (struct hclge_tx_buff_alloc_cmd *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM; i++)
+               dev_info(&hdev->pdev->dev, "tx_packet_buf_tc_%d: 0x%x\n", i,
+                        tx_buf_cmd->tx_pkt_buff[i]);
+
+       cmd = HCLGE_OPC_RX_PRIV_BUFF_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "\n");
+       rx_buf_cmd = (struct hclge_rx_priv_buff_cmd *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM; i++)
+               dev_info(&hdev->pdev->dev, "rx_packet_buf_tc_%d: 0x%x\n", i,
+                        rx_buf_cmd->buf_num[i]);
+
+       dev_info(&hdev->pdev->dev, "rx_share_buf: 0x%x\n",
+                rx_buf_cmd->shared_buf);
+
+       cmd = HCLGE_OPC_RX_PRIV_WL_ALLOC;
+       hclge_cmd_setup_basic_desc(&desc[0], cmd, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 2);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "\n");
+       rx_priv_wl = (struct hclge_rx_priv_wl_buf *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_priv_wl_tc_%d: high: 0x%x, low: 0x%x\n", i,
+                        rx_priv_wl->tc_wl[i].high, rx_priv_wl->tc_wl[i].low);
+
+       rx_priv_wl = (struct hclge_rx_priv_wl_buf *)desc[1].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_priv_wl_tc_%d: high: 0x%x, low: 0x%x\n", i + 4,
+                        rx_priv_wl->tc_wl[i].high, rx_priv_wl->tc_wl[i].low);
+
+       cmd = HCLGE_OPC_RX_COM_THRD_ALLOC;
+       hclge_cmd_setup_basic_desc(&desc[0], cmd, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 2);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       dev_info(&hdev->pdev->dev, "\n");
+       rx_com_thrd = (struct hclge_rx_com_thrd *)desc[0].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_com_thrd_tc_%d: high: 0x%x, low: 0x%x\n", i,
+                        rx_com_thrd->com_thrd[i].high,
+                        rx_com_thrd->com_thrd[i].low);
+
+       rx_com_thrd = (struct hclge_rx_com_thrd *)desc[1].data;
+       for (i = 0; i < HCLGE_TC_NUM_ONE_DESC; i++)
+               dev_info(&hdev->pdev->dev,
+                        "rx_com_thrd_tc_%d: high: 0x%x, low: 0x%x\n", i + 4,
+                        rx_com_thrd->com_thrd[i].high,
+                        rx_com_thrd->com_thrd[i].low);
+
+       cmd = HCLGE_OPC_RX_COM_WL_ALLOC;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       rx_com_wl = (struct hclge_rx_com_wl *)desc[0].data;
+       dev_info(&hdev->pdev->dev, "\n");
+       dev_info(&hdev->pdev->dev, "rx_com_wl: high: 0x%x, low: 0x%x\n",
+                rx_com_wl->com_wl.high, rx_com_wl->com_wl.low);
+
+       cmd = HCLGE_OPC_RX_GBL_PKT_CNT;
+       hclge_cmd_setup_basic_desc(desc, cmd, true);
+       ret = hclge_cmd_send(&hdev->hw, desc, 1);
+       if (ret)
+               goto err_qos_cmd_send;
+
+       rx_packet_cnt = (struct hclge_rx_com_wl *)desc[0].data;
+       dev_info(&hdev->pdev->dev,
+                "rx_global_packet_cnt: high: 0x%x, low: 0x%x\n",
+                rx_packet_cnt->com_wl.high, rx_packet_cnt->com_wl.low);
+
+       return;
+
+err_qos_cmd_send:
+       dev_err(&hdev->pdev->dev,
+               "dump qos buf cfg fail(0x%x), status is %d\n", cmd, ret);
+}
+
+static void hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage,
+                                  bool sel_x, u32 loc)
+{
+       struct hclge_fd_tcam_config_1_cmd *req1;
+       struct hclge_fd_tcam_config_2_cmd *req2;
+       struct hclge_fd_tcam_config_3_cmd *req3;
+       struct hclge_desc desc[3];
+       int ret, i;
+       u32 *req;
+
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, true);
+       desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, true);
+
+       req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data;
+       req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data;
+       req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data;
+
+       req1->stage  = stage;
+       req1->xy_sel = sel_x ? 1 : 0;
+       req1->index  = cpu_to_le32(loc);
+
+       ret = hclge_cmd_send(&hdev->hw, desc, 3);
+       if (ret)
+               return;
+
+       dev_info(&hdev->pdev->dev, " read result tcam key %s(%u):\n",
+                sel_x ? "x" : "y", loc);
+
+       req = (u32 *)req1->tcam_data;
+       for (i = 0; i < 2; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+
+       req = (u32 *)req2->tcam_data;
+       for (i = 0; i < 6; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+
+       req = (u32 *)req3->tcam_data;
+       for (i = 0; i < 5; i++)
+               dev_info(&hdev->pdev->dev, "%08x\n", *req++);
+}
+
+static void hclge_dbg_fd_tcam(struct hclge_dev *hdev)
+{
+       u32 i;
+
+       for (i = 0; i < hdev->fd_cfg.rule_num[0]; i++) {
+               hclge_dbg_fd_tcam_read(hdev, 0, true, i);
+               hclge_dbg_fd_tcam_read(hdev, 0, false, i);
+       }
+}
+
+int hclge_dbg_run_cmd(struct hnae3_handle *handle, char *cmd_buf)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       if (strncmp(cmd_buf, "dump fd tcam", 12) == 0) {
+               hclge_dbg_fd_tcam(hdev);
+       } else if (strncmp(cmd_buf, "dump tc", 7) == 0) {
+               hclge_dbg_dump_tc(hdev);
+       } else if (strncmp(cmd_buf, "dump tm", 7) == 0) {
+               hclge_dbg_dump_tm(hdev);
+       } else if (strncmp(cmd_buf, "dump qos pause cfg", 18) == 0) {
+               hclge_dbg_dump_qos_pause_cfg(hdev);
+       } else if (strncmp(cmd_buf, "dump qos pri map", 16) == 0) {
+               hclge_dbg_dump_qos_pri_map(hdev);
+       } else if (strncmp(cmd_buf, "dump qos buf cfg", 16) == 0) {
+               hclge_dbg_dump_qos_buf_cfg(hdev);
+       } else {
+               dev_info(&hdev->pdev->dev, "unknown command\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.h
new file mode 100644 (file)
index 0000000..50fd0b1
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+#ifndef __HCLGE_DEBUGFS_H
+#define __HCLGE_DEBUGFS_H
+
+#pragma pack(1)
+
+struct hclge_qos_pri_map_cmd {
+       u8 pri0_tc  : 4,
+          pri1_tc  : 4;
+       u8 pri2_tc  : 4,
+          pri3_tc  : 4;
+       u8 pri4_tc  : 4,
+          pri5_tc  : 4;
+       u8 pri6_tc  : 4,
+          pri7_tc  : 4;
+       u8 vlan_pri : 4,
+          rev      : 4;
+};
+
+#pragma pack()
+#endif
index 123c37e..77deea0 100644 (file)
@@ -4,78 +4,39 @@
 #include "hclge_err.h"
 
 static const struct hclge_hw_error hclge_imp_tcm_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "imp_itcm0_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "imp_itcm0_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "imp_itcm1_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "imp_itcm1_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "imp_itcm2_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "imp_itcm2_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "imp_itcm3_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "imp_itcm3_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "imp_dtcm0_mem0_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "imp_dtcm0_mem0_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "imp_dtcm0_mem1_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "imp_dtcm0_mem1_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "imp_dtcm1_mem0_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "imp_dtcm1_mem0_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "imp_dtcm1_mem1_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "imp_dtcm1_mem1_ecc_mbit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_imp_itcm4_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "imp_itcm4_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "imp_itcm4_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "imp_itcm4_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
 static const struct hclge_hw_error hclge_cmdq_nic_mem_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "cmdq_nic_rx_depth_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "cmdq_nic_rx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "cmdq_nic_tx_depth_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "cmdq_nic_tx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "cmdq_nic_rx_tail_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "cmdq_nic_rx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "cmdq_nic_tx_tail_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "cmdq_nic_tx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "cmdq_nic_rx_head_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "cmdq_nic_rx_head_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "cmdq_nic_tx_head_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "cmdq_nic_tx_head_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "cmdq_nic_rx_addr_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "cmdq_nic_rx_addr_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "cmdq_nic_tx_addr_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "cmdq_nic_tx_addr_ecc_mbit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_cmdq_rocee_mem_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "cmdq_rocee_rx_depth_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "cmdq_rocee_rx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "cmdq_rocee_tx_depth_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "cmdq_rocee_tx_depth_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "cmdq_rocee_rx_tail_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "cmdq_rocee_rx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "cmdq_rocee_tx_tail_ecc_1bit_err" },
-       { .int_msk = BIT(7), .msg = "cmdq_rocee_tx_tail_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "cmdq_rocee_rx_head_ecc_1bit_err" },
-       { .int_msk = BIT(9), .msg = "cmdq_rocee_rx_head_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "cmdq_rocee_tx_head_ecc_1bit_err" },
-       { .int_msk = BIT(11), .msg = "cmdq_rocee_tx_head_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "cmdq_rocee_rx_addr_ecc_1bit_err" },
-       { .int_msk = BIT(13), .msg = "cmdq_rocee_rx_addr_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "cmdq_rocee_tx_addr_ecc_1bit_err" },
-       { .int_msk = BIT(15), .msg = "cmdq_rocee_tx_addr_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "cmdq_rocee_rx_depth_ecc_mbit_err" },
+       { .int_msk = BIT(19), .msg = "cmdq_rocee_tx_depth_ecc_mbit_err" },
+       { .int_msk = BIT(21), .msg = "cmdq_rocee_rx_tail_ecc_mbit_err" },
+       { .int_msk = BIT(23), .msg = "cmdq_rocee_tx_tail_ecc_mbit_err" },
+       { .int_msk = BIT(25), .msg = "cmdq_rocee_rx_head_ecc_mbit_err" },
+       { .int_msk = BIT(27), .msg = "cmdq_rocee_tx_head_ecc_mbit_err" },
+       { .int_msk = BIT(29), .msg = "cmdq_rocee_rx_addr_ecc_mbit_err" },
+       { .int_msk = BIT(31), .msg = "cmdq_rocee_tx_addr_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
 static const struct hclge_hw_error hclge_tqp_int_ecc_int[] = {
-       { .int_msk = BIT(0), .msg = "tqp_int_cfg_even_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "tqp_int_cfg_odd_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "tqp_int_ctrl_even_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "tqp_int_ctrl_odd_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "tx_que_scan_int_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "rx_que_scan_int_ecc_1bit_err" },
        { .int_msk = BIT(6), .msg = "tqp_int_cfg_even_ecc_mbit_err" },
        { .int_msk = BIT(7), .msg = "tqp_int_cfg_odd_ecc_mbit_err" },
        { .int_msk = BIT(8), .msg = "tqp_int_ctrl_even_ecc_mbit_err" },
@@ -85,15 +46,19 @@ static const struct hclge_hw_error hclge_tqp_int_ecc_int[] = {
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_igu_com_err_int[] = {
+static const struct hclge_hw_error hclge_msix_sram_ecc_int[] = {
+       { .int_msk = BIT(1), .msg = "msix_nic_ecc_mbit_err" },
+       { .int_msk = BIT(3), .msg = "msix_rocee_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_igu_int[] = {
        { .int_msk = BIT(0), .msg = "igu_rx_buf0_ecc_mbit_err" },
-       { .int_msk = BIT(1), .msg = "igu_rx_buf0_ecc_1bit_err" },
        { .int_msk = BIT(2), .msg = "igu_rx_buf1_ecc_mbit_err" },
-       { .int_msk = BIT(3), .msg = "igu_rx_buf1_ecc_1bit_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_igu_egu_tnl_err_int[] = {
+static const struct hclge_hw_error hclge_igu_egu_tnl_int[] = {
        { .int_msk = BIT(0), .msg = "rx_buf_overflow" },
        { .int_msk = BIT(1), .msg = "rx_stp_fifo_overflow" },
        { .int_msk = BIT(2), .msg = "rx_stp_fifo_undeflow" },
@@ -104,51 +69,11 @@ static const struct hclge_hw_error hclge_igu_egu_tnl_err_int[] = {
 };
 
 static const struct hclge_hw_error hclge_ncsi_err_int[] = {
-       { .int_msk = BIT(0), .msg = "ncsi_tx_ecc_1bit_err" },
        { .int_msk = BIT(1), .msg = "ncsi_tx_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_mpf_int0[] = {
-       { .int_msk = BIT(0), .msg = "vf_vlan_ad_mem_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "umv_mcast_group_mem_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "umv_key_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "umv_key_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "umv_key_mem2_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "umv_key_mem3_ecc_1bit_err" },
-       { .int_msk = BIT(6), .msg = "umv_ad_mem_ecc_1bit_err" },
-       { .int_msk = BIT(7), .msg = "rss_tc_mode_mem_ecc_1bit_err" },
-       { .int_msk = BIT(8), .msg = "rss_idt_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(9), .msg = "rss_idt_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(10), .msg = "rss_idt_mem2_ecc_1bit_err" },
-       { .int_msk = BIT(11), .msg = "rss_idt_mem3_ecc_1bit_err" },
-       { .int_msk = BIT(12), .msg = "rss_idt_mem4_ecc_1bit_err" },
-       { .int_msk = BIT(13), .msg = "rss_idt_mem5_ecc_1bit_err" },
-       { .int_msk = BIT(14), .msg = "rss_idt_mem6_ecc_1bit_err" },
-       { .int_msk = BIT(15), .msg = "rss_idt_mem7_ecc_1bit_err" },
-       { .int_msk = BIT(16), .msg = "rss_idt_mem8_ecc_1bit_err" },
-       { .int_msk = BIT(17), .msg = "rss_idt_mem9_ecc_1bit_err" },
-       { .int_msk = BIT(18), .msg = "rss_idt_mem10_ecc_1bit_err" },
-       { .int_msk = BIT(19), .msg = "rss_idt_mem11_ecc_1bit_err" },
-       { .int_msk = BIT(20), .msg = "rss_idt_mem12_ecc_1bit_err" },
-       { .int_msk = BIT(21), .msg = "rss_idt_mem13_ecc_1bit_err" },
-       { .int_msk = BIT(22), .msg = "rss_idt_mem14_ecc_1bit_err" },
-       { .int_msk = BIT(23), .msg = "rss_idt_mem15_ecc_1bit_err" },
-       { .int_msk = BIT(24), .msg = "port_vlan_mem_ecc_1bit_err" },
-       { .int_msk = BIT(25), .msg = "mcast_linear_table_mem_ecc_1bit_err" },
-       { .int_msk = BIT(26), .msg = "mcast_result_mem_ecc_1bit_err" },
-       { .int_msk = BIT(27),
-               .msg = "flow_director_ad_mem0_ecc_1bit_err" },
-       { .int_msk = BIT(28),
-               .msg = "flow_director_ad_mem1_ecc_1bit_err" },
-       { .int_msk = BIT(29),
-               .msg = "rx_vlan_tag_memory_ecc_1bit_err" },
-       { .int_msk = BIT(30),
-               .msg = "Tx_UP_mapping_config_mem_ecc_1bit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_ppp_mpf_int1[] = {
+static const struct hclge_hw_error hclge_ppp_mpf_abnormal_int_st1[] = {
        { .int_msk = BIT(0), .msg = "vf_vlan_ad_mem_ecc_mbit_err" },
        { .int_msk = BIT(1), .msg = "umv_mcast_group_mem_ecc_mbit_err" },
        { .int_msk = BIT(2), .msg = "umv_key_mem0_ecc_mbit_err" },
@@ -187,23 +112,13 @@ static const struct hclge_hw_error hclge_ppp_mpf_int1[] = {
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_pf_int[] = {
-       { .int_msk = BIT(0), .msg = "Tx_vlan_tag_err" },
+static const struct hclge_hw_error hclge_ppp_pf_abnormal_int[] = {
+       { .int_msk = BIT(0), .msg = "tx_vlan_tag_err" },
        { .int_msk = BIT(1), .msg = "rss_list_tc_unassigned_queue_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_ppp_mpf_int2[] = {
-       { .int_msk = BIT(0), .msg = "hfs_fifo_mem_ecc_1bit_err" },
-       { .int_msk = BIT(1), .msg = "rslt_descr_fifo_mem_ecc_1bit_err" },
-       { .int_msk = BIT(2), .msg = "tx_vlan_tag_mem_ecc_1bit_err" },
-       { .int_msk = BIT(3), .msg = "FD_CN0_memory_ecc_1bit_err" },
-       { .int_msk = BIT(4), .msg = "FD_CN1_memory_ecc_1bit_err" },
-       { .int_msk = BIT(5), .msg = "GRO_AD_memory_ecc_1bit_err" },
-       { /* sentinel */ }
-};
-
-static const struct hclge_hw_error hclge_ppp_mpf_int3[] = {
+static const struct hclge_hw_error hclge_ppp_mpf_abnormal_int_st3[] = {
        { .int_msk = BIT(0), .msg = "hfs_fifo_mem_ecc_mbit_err" },
        { .int_msk = BIT(1), .msg = "rslt_descr_fifo_mem_ecc_mbit_err" },
        { .int_msk = BIT(2), .msg = "tx_vlan_tag_mem_ecc_mbit_err" },
@@ -213,145 +128,248 @@ static const struct hclge_hw_error hclge_ppp_mpf_int3[] = {
        { /* sentinel */ }
 };
 
-struct hclge_tm_sch_ecc_info {
-       const char *name;
-};
-
-static const struct hclge_tm_sch_ecc_info hclge_tm_sch_ecc_err[7][15] = {
-       {
-               { .name = "QSET_QUEUE_CTRL:PRI_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_LEN TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_HPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:QS_LINKLIST TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPA_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:SPB_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRA_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:WRRB_TPTR TAB" },
-               { .name = "QSET_QUEUE_CTRL:QS_DEFICITCNT TAB" },
-       },
-       {
-               { .name = "ROCE_QUEUE_CTRL:QS_LEN TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QS_TPTR TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QS_HPTR TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QLINKLIST TAB" },
-               { .name = "ROCE_QUEUE_CTRL:QCLEN TAB" },
-       },
-       {
-               { .name = "NIC_QUEUE_CTRL:QS_LEN TAB" },
-               { .name = "NIC_QUEUE_CTRL:QS_TPTR TAB" },
-               { .name = "NIC_QUEUE_CTRL:QS_HPTR TAB" },
-               { .name = "NIC_QUEUE_CTRL:QLINKLIST TAB" },
-               { .name = "NIC_QUEUE_CTRL:QCLEN TAB" },
-       },
-       {
-               { .name = "RAM_CFG_CTRL:CSHAP TAB" },
-               { .name = "RAM_CFG_CTRL:PSHAP TAB" },
-       },
-       {
-               { .name = "SHAPER_CTRL:PSHAP TAB" },
-       },
-       {
-               { .name = "MSCH_CTRL" },
-       },
-       {
-               { .name = "TOP_CTRL" },
-       },
-};
-
-static const struct hclge_hw_error hclge_tm_sch_err_int[] = {
-       { .int_msk = BIT(0), .msg = "tm_sch_ecc_1bit_err" },
+static const struct hclge_hw_error hclge_tm_sch_rint[] = {
        { .int_msk = BIT(1), .msg = "tm_sch_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "tm_sch_port_shap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(3), .msg = "tm_sch_port_shap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(4), .msg = "tm_sch_pg_pshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(5), .msg = "tm_sch_pg_pshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(6), .msg = "tm_sch_pg_cshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(7), .msg = "tm_sch_pg_cshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(8), .msg = "tm_sch_pri_pshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(9), .msg = "tm_sch_pri_pshap_sub_fifo_rd_empty_err" },
-       { .int_msk = BIT(10), .msg = "tm_sch_pri_cshap_sub_fifo_wr_full_err" },
-       { .int_msk = BIT(11), .msg = "tm_sch_pri_cshap_sub_fifo_rd_empty_err" },
+       { .int_msk = BIT(2), .msg = "tm_sch_port_shap_sub_fifo_wr_err" },
+       { .int_msk = BIT(3), .msg = "tm_sch_port_shap_sub_fifo_rd_err" },
+       { .int_msk = BIT(4), .msg = "tm_sch_pg_pshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(5), .msg = "tm_sch_pg_pshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(6), .msg = "tm_sch_pg_cshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(7), .msg = "tm_sch_pg_cshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(8), .msg = "tm_sch_pri_pshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(9), .msg = "tm_sch_pri_pshap_sub_fifo_rd_err" },
+       { .int_msk = BIT(10), .msg = "tm_sch_pri_cshap_sub_fifo_wr_err" },
+       { .int_msk = BIT(11), .msg = "tm_sch_pri_cshap_sub_fifo_rd_err" },
        { .int_msk = BIT(12),
-         .msg = "tm_sch_port_shap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_port_shap_offset_fifo_wr_err" },
        { .int_msk = BIT(13),
-         .msg = "tm_sch_port_shap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_port_shap_offset_fifo_rd_err" },
        { .int_msk = BIT(14),
-         .msg = "tm_sch_pg_pshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pg_pshap_offset_fifo_wr_err" },
        { .int_msk = BIT(15),
-         .msg = "tm_sch_pg_pshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pg_pshap_offset_fifo_rd_err" },
        { .int_msk = BIT(16),
-         .msg = "tm_sch_pg_cshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pg_cshap_offset_fifo_wr_err" },
        { .int_msk = BIT(17),
-         .msg = "tm_sch_pg_cshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pg_cshap_offset_fifo_rd_err" },
        { .int_msk = BIT(18),
-         .msg = "tm_sch_pri_pshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pri_pshap_offset_fifo_wr_err" },
        { .int_msk = BIT(19),
-         .msg = "tm_sch_pri_pshap_offset_fifo_rd_empty_err" },
+         .msg = "tm_sch_pri_pshap_offset_fifo_rd_err" },
        { .int_msk = BIT(20),
-         .msg = "tm_sch_pri_cshap_offset_fifo_wr_full_err" },
+         .msg = "tm_sch_pri_cshap_offset_fifo_wr_err" },
        { .int_msk = BIT(21),
-         .msg = "tm_sch_pri_cshap_offset_fifo_rd_empty_err" },
-       { .int_msk = BIT(22), .msg = "tm_sch_rq_fifo_wr_full_err" },
-       { .int_msk = BIT(23), .msg = "tm_sch_rq_fifo_rd_empty_err" },
-       { .int_msk = BIT(24), .msg = "tm_sch_nq_fifo_wr_full_err" },
-       { .int_msk = BIT(25), .msg = "tm_sch_nq_fifo_rd_empty_err" },
-       { .int_msk = BIT(26), .msg = "tm_sch_roce_up_fifo_wr_full_err" },
-       { .int_msk = BIT(27), .msg = "tm_sch_roce_up_fifo_rd_empty_err" },
-       { .int_msk = BIT(28), .msg = "tm_sch_rcb_byte_fifo_wr_full_err" },
-       { .int_msk = BIT(29), .msg = "tm_sch_rcb_byte_fifo_rd_empty_err" },
-       { .int_msk = BIT(30), .msg = "tm_sch_ssu_byte_fifo_wr_full_err" },
-       { .int_msk = BIT(31), .msg = "tm_sch_ssu_byte_fifo_rd_empty_err" },
+         .msg = "tm_sch_pri_cshap_offset_fifo_rd_err" },
+       { .int_msk = BIT(22), .msg = "tm_sch_rq_fifo_wr_err" },
+       { .int_msk = BIT(23), .msg = "tm_sch_rq_fifo_rd_err" },
+       { .int_msk = BIT(24), .msg = "tm_sch_nq_fifo_wr_err" },
+       { .int_msk = BIT(25), .msg = "tm_sch_nq_fifo_rd_err" },
+       { .int_msk = BIT(26), .msg = "tm_sch_roce_up_fifo_wr_err" },
+       { .int_msk = BIT(27), .msg = "tm_sch_roce_up_fifo_rd_err" },
+       { .int_msk = BIT(28), .msg = "tm_sch_rcb_byte_fifo_wr_err" },
+       { .int_msk = BIT(29), .msg = "tm_sch_rcb_byte_fifo_rd_err" },
+       { .int_msk = BIT(30), .msg = "tm_sch_ssu_byte_fifo_wr_err" },
+       { .int_msk = BIT(31), .msg = "tm_sch_ssu_byte_fifo_rd_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_qcn_fifo_rint[] = {
+       { .int_msk = BIT(0), .msg = "qcn_shap_gp0_sch_fifo_rd_err" },
+       { .int_msk = BIT(1), .msg = "qcn_shap_gp0_sch_fifo_wr_err" },
+       { .int_msk = BIT(2), .msg = "qcn_shap_gp1_sch_fifo_rd_err" },
+       { .int_msk = BIT(3), .msg = "qcn_shap_gp1_sch_fifo_wr_err" },
+       { .int_msk = BIT(4), .msg = "qcn_shap_gp2_sch_fifo_rd_err" },
+       { .int_msk = BIT(5), .msg = "qcn_shap_gp2_sch_fifo_wr_err" },
+       { .int_msk = BIT(6), .msg = "qcn_shap_gp3_sch_fifo_rd_err" },
+       { .int_msk = BIT(7), .msg = "qcn_shap_gp3_sch_fifo_wr_err" },
+       { .int_msk = BIT(8), .msg = "qcn_shap_gp0_offset_fifo_rd_err" },
+       { .int_msk = BIT(9), .msg = "qcn_shap_gp0_offser_fifo_wr_err" },
+       { .int_msk = BIT(10), .msg = "qcn_shap_gp1_offset_fifo_rd_err" },
+       { .int_msk = BIT(11), .msg = "qcn_shap_gp1_offset_fifo_wr_err" },
+       { .int_msk = BIT(12), .msg = "qcn_shap_gp2_offset_fifo_rd_err" },
+       { .int_msk = BIT(13), .msg = "qcn_shap_gp2_offset_fifo_wr_err" },
+       { .int_msk = BIT(14), .msg = "qcn_shap_gp3_offset_fifo_rd_err" },
+       { .int_msk = BIT(15), .msg = "qcn_shap_gp3_offset_fifo_wr_err" },
+       { .int_msk = BIT(16), .msg = "qcn_byte_info_fifo_rd_err" },
+       { .int_msk = BIT(17), .msg = "qcn_byte_info_fifo_wr_err" },
        { /* sentinel */ }
 };
 
-static const struct hclge_hw_error hclge_qcn_ecc_err_int[] = {
-       { .int_msk = BIT(0), .msg = "qcn_byte_mem_ecc_1bit_err" },
+static const struct hclge_hw_error hclge_qcn_ecc_rint[] = {
        { .int_msk = BIT(1), .msg = "qcn_byte_mem_ecc_mbit_err" },
-       { .int_msk = BIT(2), .msg = "qcn_time_mem_ecc_1bit_err" },
        { .int_msk = BIT(3), .msg = "qcn_time_mem_ecc_mbit_err" },
-       { .int_msk = BIT(4), .msg = "qcn_fb_mem_ecc_1bit_err" },
        { .int_msk = BIT(5), .msg = "qcn_fb_mem_ecc_mbit_err" },
-       { .int_msk = BIT(6), .msg = "qcn_link_mem_ecc_1bit_err" },
        { .int_msk = BIT(7), .msg = "qcn_link_mem_ecc_mbit_err" },
-       { .int_msk = BIT(8), .msg = "qcn_rate_mem_ecc_1bit_err" },
        { .int_msk = BIT(9), .msg = "qcn_rate_mem_ecc_mbit_err" },
-       { .int_msk = BIT(10), .msg = "qcn_tmplt_mem_ecc_1bit_err" },
        { .int_msk = BIT(11), .msg = "qcn_tmplt_mem_ecc_mbit_err" },
-       { .int_msk = BIT(12), .msg = "qcn_shap_cfg_mem_ecc_1bit_err" },
        { .int_msk = BIT(13), .msg = "qcn_shap_cfg_mem_ecc_mbit_err" },
-       { .int_msk = BIT(14), .msg = "qcn_gp0_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(15), .msg = "qcn_gp0_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(16), .msg = "qcn_gp1_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(17), .msg = "qcn_gp1_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(18), .msg = "qcn_gp2_barrel_mem_ecc_1bit_err" },
        { .int_msk = BIT(19), .msg = "qcn_gp2_barrel_mem_ecc_mbit_err" },
-       { .int_msk = BIT(20), .msg = "qcn_gp3_barral_mem_ecc_1bit_err" },
        { .int_msk = BIT(21), .msg = "qcn_gp3_barral_mem_ecc_mbit_err" },
        { /* sentinel */ }
 };
 
-static void hclge_log_error(struct device *dev,
-                           const struct hclge_hw_error *err_list,
+static const struct hclge_hw_error hclge_mac_afifo_tnl_int[] = {
+       { .int_msk = BIT(0), .msg = "egu_cge_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(1), .msg = "egu_cge_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(2), .msg = "egu_lge_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(3), .msg = "egu_lge_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(4), .msg = "cge_igu_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(5), .msg = "cge_igu_afifo_ecc_mbit_err" },
+       { .int_msk = BIT(6), .msg = "lge_igu_afifo_ecc_1bit_err" },
+       { .int_msk = BIT(7), .msg = "lge_igu_afifo_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_mpf_abnormal_int_st2[] = {
+       { .int_msk = BIT(13), .msg = "rpu_rx_pkt_bit32_ecc_mbit_err" },
+       { .int_msk = BIT(14), .msg = "rpu_rx_pkt_bit33_ecc_mbit_err" },
+       { .int_msk = BIT(15), .msg = "rpu_rx_pkt_bit34_ecc_mbit_err" },
+       { .int_msk = BIT(16), .msg = "rpu_rx_pkt_bit35_ecc_mbit_err" },
+       { .int_msk = BIT(17), .msg = "rcb_tx_ring_ecc_mbit_err" },
+       { .int_msk = BIT(18), .msg = "rcb_rx_ring_ecc_mbit_err" },
+       { .int_msk = BIT(19), .msg = "rcb_tx_fbd_ecc_mbit_err" },
+       { .int_msk = BIT(20), .msg = "rcb_rx_ebd_ecc_mbit_err" },
+       { .int_msk = BIT(21), .msg = "rcb_tso_info_ecc_mbit_err" },
+       { .int_msk = BIT(22), .msg = "rcb_tx_int_info_ecc_mbit_err" },
+       { .int_msk = BIT(23), .msg = "rcb_rx_int_info_ecc_mbit_err" },
+       { .int_msk = BIT(24), .msg = "tpu_tx_pkt_0_ecc_mbit_err" },
+       { .int_msk = BIT(25), .msg = "tpu_tx_pkt_1_ecc_mbit_err" },
+       { .int_msk = BIT(26), .msg = "rd_bus_err" },
+       { .int_msk = BIT(27), .msg = "wr_bus_err" },
+       { .int_msk = BIT(28), .msg = "reg_search_miss" },
+       { .int_msk = BIT(29), .msg = "rx_q_search_miss" },
+       { .int_msk = BIT(30), .msg = "ooo_ecc_err_detect" },
+       { .int_msk = BIT(31), .msg = "ooo_ecc_err_multpl" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_mpf_abnormal_int_st3[] = {
+       { .int_msk = BIT(4), .msg = "gro_bd_ecc_mbit_err" },
+       { .int_msk = BIT(5), .msg = "gro_context_ecc_mbit_err" },
+       { .int_msk = BIT(6), .msg = "rx_stash_cfg_ecc_mbit_err" },
+       { .int_msk = BIT(7), .msg = "axi_rd_fbd_ecc_mbit_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ppu_pf_abnormal_int[] = {
+       { .int_msk = BIT(0), .msg = "over_8bd_no_fe" },
+       { .int_msk = BIT(1), .msg = "tso_mss_cmp_min_err" },
+       { .int_msk = BIT(2), .msg = "tso_mss_cmp_max_err" },
+       { .int_msk = BIT(3), .msg = "tx_rd_fbd_poison" },
+       { .int_msk = BIT(4), .msg = "rx_rd_ebd_poison" },
+       { .int_msk = BIT(5), .msg = "buf_wait_timeout" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_com_err_int[] = {
+       { .int_msk = BIT(0), .msg = "buf_sum_err" },
+       { .int_msk = BIT(1), .msg = "ppp_mb_num_err" },
+       { .int_msk = BIT(2), .msg = "ppp_mbid_err" },
+       { .int_msk = BIT(3), .msg = "ppp_rlt_mac_err" },
+       { .int_msk = BIT(4), .msg = "ppp_rlt_host_err" },
+       { .int_msk = BIT(5), .msg = "cks_edit_position_err" },
+       { .int_msk = BIT(6), .msg = "cks_edit_condition_err" },
+       { .int_msk = BIT(7), .msg = "vlan_edit_condition_err" },
+       { .int_msk = BIT(8), .msg = "vlan_num_ot_err" },
+       { .int_msk = BIT(9), .msg = "vlan_num_in_err" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_port_based_err_int[] = {
+       { .int_msk = BIT(0), .msg = "roc_pkt_without_key_port" },
+       { .int_msk = BIT(1), .msg = "tpu_pkt_without_key_port" },
+       { .int_msk = BIT(2), .msg = "igu_pkt_without_key_port" },
+       { .int_msk = BIT(3), .msg = "roc_eof_mis_match_port" },
+       { .int_msk = BIT(4), .msg = "tpu_eof_mis_match_port" },
+       { .int_msk = BIT(5), .msg = "igu_eof_mis_match_port" },
+       { .int_msk = BIT(6), .msg = "roc_sof_mis_match_port" },
+       { .int_msk = BIT(7), .msg = "tpu_sof_mis_match_port" },
+       { .int_msk = BIT(8), .msg = "igu_sof_mis_match_port" },
+       { .int_msk = BIT(11), .msg = "ets_rd_int_rx_port" },
+       { .int_msk = BIT(12), .msg = "ets_wr_int_rx_port" },
+       { .int_msk = BIT(13), .msg = "ets_rd_int_tx_port" },
+       { .int_msk = BIT(14), .msg = "ets_wr_int_tx_port" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_fifo_overflow_int[] = {
+       { .int_msk = BIT(0), .msg = "ig_mac_inf_int" },
+       { .int_msk = BIT(1), .msg = "ig_host_inf_int" },
+       { .int_msk = BIT(2), .msg = "ig_roc_buf_int" },
+       { .int_msk = BIT(3), .msg = "ig_host_data_fifo_int" },
+       { .int_msk = BIT(4), .msg = "ig_host_key_fifo_int" },
+       { .int_msk = BIT(5), .msg = "tx_qcn_fifo_int" },
+       { .int_msk = BIT(6), .msg = "rx_qcn_fifo_int" },
+       { .int_msk = BIT(7), .msg = "tx_pf_rd_fifo_int" },
+       { .int_msk = BIT(8), .msg = "rx_pf_rd_fifo_int" },
+       { .int_msk = BIT(9), .msg = "qm_eof_fifo_int" },
+       { .int_msk = BIT(10), .msg = "mb_rlt_fifo_int" },
+       { .int_msk = BIT(11), .msg = "dup_uncopy_fifo_int" },
+       { .int_msk = BIT(12), .msg = "dup_cnt_rd_fifo_int" },
+       { .int_msk = BIT(13), .msg = "dup_cnt_drop_fifo_int" },
+       { .int_msk = BIT(14), .msg = "dup_cnt_wrb_fifo_int" },
+       { .int_msk = BIT(15), .msg = "host_cmd_fifo_int" },
+       { .int_msk = BIT(16), .msg = "mac_cmd_fifo_int" },
+       { .int_msk = BIT(17), .msg = "host_cmd_bitmap_empty_int" },
+       { .int_msk = BIT(18), .msg = "mac_cmd_bitmap_empty_int" },
+       { .int_msk = BIT(19), .msg = "dup_bitmap_empty_int" },
+       { .int_msk = BIT(20), .msg = "out_queue_bitmap_empty_int" },
+       { .int_msk = BIT(21), .msg = "bank2_bitmap_empty_int" },
+       { .int_msk = BIT(22), .msg = "bank1_bitmap_empty_int" },
+       { .int_msk = BIT(23), .msg = "bank0_bitmap_empty_int" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_ets_tcg_int[] = {
+       { .int_msk = BIT(0), .msg = "ets_rd_int_rx_tcg" },
+       { .int_msk = BIT(1), .msg = "ets_wr_int_rx_tcg" },
+       { .int_msk = BIT(2), .msg = "ets_rd_int_tx_tcg" },
+       { .int_msk = BIT(3), .msg = "ets_wr_int_tx_tcg" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_ssu_port_based_pf_int[] = {
+       { .int_msk = BIT(0), .msg = "roc_pkt_without_key_port" },
+       { .int_msk = BIT(9), .msg = "low_water_line_err_port" },
+       { .int_msk = BIT(10), .msg = "hi_water_line_err_port" },
+       { /* sentinel */ }
+};
+
+static const struct hclge_hw_error hclge_rocee_qmm_ovf_err_int[] = {
+       { .int_msk = 0, .msg = "rocee qmm ovf: sgid invalid err" },
+       { .int_msk = 0x4, .msg = "rocee qmm ovf: sgid ovf err" },
+       { .int_msk = 0x8, .msg = "rocee qmm ovf: smac invalid err" },
+       { .int_msk = 0xC, .msg = "rocee qmm ovf: smac ovf err" },
+       { .int_msk = 0x10, .msg = "rocee qmm ovf: cqc invalid err" },
+       { .int_msk = 0x11, .msg = "rocee qmm ovf: cqc ovf err" },
+       { .int_msk = 0x12, .msg = "rocee qmm ovf: cqc hopnum err" },
+       { .int_msk = 0x13, .msg = "rocee qmm ovf: cqc ba0 err" },
+       { .int_msk = 0x14, .msg = "rocee qmm ovf: srqc invalid err" },
+       { .int_msk = 0x15, .msg = "rocee qmm ovf: srqc ovf err" },
+       { .int_msk = 0x16, .msg = "rocee qmm ovf: srqc hopnum err" },
+       { .int_msk = 0x17, .msg = "rocee qmm ovf: srqc ba0 err" },
+       { .int_msk = 0x18, .msg = "rocee qmm ovf: mpt invalid err" },
+       { .int_msk = 0x19, .msg = "rocee qmm ovf: mpt ovf err" },
+       { .int_msk = 0x1A, .msg = "rocee qmm ovf: mpt hopnum err" },
+       { .int_msk = 0x1B, .msg = "rocee qmm ovf: mpt ba0 err" },
+       { .int_msk = 0x1C, .msg = "rocee qmm ovf: qpc invalid err" },
+       { .int_msk = 0x1D, .msg = "rocee qmm ovf: qpc ovf err" },
+       { .int_msk = 0x1E, .msg = "rocee qmm ovf: qpc hopnum err" },
+       { .int_msk = 0x1F, .msg = "rocee qmm ovf: qpc ba0 err" },
+       { /* sentinel */ }
+};
+
+static void hclge_log_error(struct device *dev, char *reg,
+                           const struct hclge_hw_error *err,
                            u32 err_sts)
 {
-       const struct hclge_hw_error *err;
-       int i = 0;
-
-       while (err_list[i].msg) {
-               err = &err_list[i];
-               if (!(err->int_msk & err_sts)) {
-                       i++;
-                       continue;
-               }
-               dev_warn(dev, "%s [error status=0x%x] found\n",
-                        err->msg, err_sts);
-               i++;
+       while (err->msg) {
+               if (err->int_msk & err_sts)
+                       dev_warn(dev, "%s %s found [error status=0x%x]\n",
+                                reg, err->msg, err_sts);
+               err++;
        }
 }
 
@@ -391,96 +409,44 @@ static int hclge_cmd_query_error(struct hclge_dev *hdev,
        return ret;
 }
 
-/* hclge_cmd_clear_error: clear the error status
- * @hdev: pointer to struct hclge_dev
- * @desc: descriptor for describing the command
- * @desc_src: prefilled descriptor from the previous command for reusing
- * @cmd:  command opcode
- * @flag: flag for extended command structure
- *
- * This function clear the error status in the hw register/s using command
- */
-static int hclge_cmd_clear_error(struct hclge_dev *hdev,
-                                struct hclge_desc *desc,
-                                struct hclge_desc *desc_src,
-                                u32 cmd, u16 flag)
-{
-       struct device *dev = &hdev->pdev->dev;
-       int num = 1;
-       int ret, i;
-
-       if (cmd) {
-               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
-               if (flag) {
-                       desc[0].flag |= cpu_to_le16(flag);
-                       hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
-                       num = 2;
-               }
-               if (desc_src) {
-                       for (i = 0; i < 6; i++) {
-                               desc[0].data[i] = desc_src[0].data[i];
-                               if (flag)
-                                       desc[1].data[i] = desc_src[1].data[i];
-                       }
-               }
-       } else {
-               hclge_cmd_reuse_desc(&desc[0], false);
-               if (flag) {
-                       desc[0].flag |= cpu_to_le16(flag);
-                       hclge_cmd_reuse_desc(&desc[1], false);
-                       num = 2;
-               }
-       }
-       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
-       if (ret)
-               dev_err(dev, "clear error cmd failed (%d)\n", ret);
-
-       return ret;
-}
-
-static int hclge_enable_common_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_common_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc[2];
        int ret;
 
+       /* configure common error interrupts */
        hclge_cmd_setup_basic_desc(&desc[0], HCLGE_COMMON_ECC_INT_CFG, false);
        desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
        hclge_cmd_setup_basic_desc(&desc[1], HCLGE_COMMON_ECC_INT_CFG, false);
 
        if (en) {
-               /* enable COMMON error interrupts */
                desc[0].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_ERR_INT_EN);
                desc[0].data[2] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_ERR_INT_EN |
                                        HCLGE_CMDQ_ROCEE_ECC_ERR_INT_EN);
                desc[0].data[3] = cpu_to_le32(HCLGE_IMP_RD_POISON_ERR_INT_EN);
-               desc[0].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN);
+               desc[0].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN |
+                                             HCLGE_MSIX_SRAM_ECC_ERR_INT_EN);
                desc[0].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_ERR_INT_EN);
-       } else {
-               /* disable COMMON error interrupts */
-               desc[0].data[0] = 0;
-               desc[0].data[2] = 0;
-               desc[0].data[3] = 0;
-               desc[0].data[4] = 0;
-               desc[0].data[5] = 0;
        }
+
        desc[1].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_ERR_INT_EN_MASK);
        desc[1].data[2] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_ERR_INT_EN_MASK |
                                HCLGE_CMDQ_ROCEE_ECC_ERR_INT_EN_MASK);
        desc[1].data[3] = cpu_to_le32(HCLGE_IMP_RD_POISON_ERR_INT_EN_MASK);
-       desc[1].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN_MASK);
+       desc[1].data[4] = cpu_to_le32(HCLGE_TQP_ECC_ERR_INT_EN_MASK |
+                                     HCLGE_MSIX_SRAM_ECC_ERR_INT_EN_MASK);
        desc[1].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to enable/disable COMMON err interrupts\n",
-                       ret);
+                       "fail(%d) to configure common err interrupts\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_ncsi_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_ncsi_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
@@ -489,74 +455,65 @@ static int hclge_enable_ncsi_error(struct hclge_dev *hdev, bool en)
        if (hdev->pdev->revision < 0x21)
                return 0;
 
-       /* enable/disable NCSI  error interrupts */
+       /* configure NCSI error interrupts */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_NCSI_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_NCSI_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to enable/disable NCSI error interrupts\n",
-                       ret);
+                       "fail(%d) to configure  NCSI error interrupts\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_igu_egu_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_igu_egu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* enable/disable error interrupts */
+       /* configure IGU,EGU error interrupts */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_IGU_COMMON_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_IGU_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
+
        desc.data[1] = cpu_to_le32(HCLGE_IGU_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
                dev_err(dev,
-                       "failed(%d) to enable/disable IGU common interrupts\n",
-                       ret);
+                       "fail(%d) to configure IGU common interrupts\n", ret);
                return ret;
        }
 
        hclge_cmd_setup_basic_desc(&desc, HCLGE_IGU_EGU_TNL_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_IGU_TNL_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
+
        desc.data[1] = cpu_to_le32(HCLGE_IGU_TNL_ERR_INT_EN_MASK);
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
                dev_err(dev,
-                       "failed(%d) to enable/disable IGU-EGU TNL interrupts\n",
-                       ret);
+                       "fail(%d) to configure IGU-EGU TNL interrupts\n", ret);
                return ret;
        }
 
-       ret = hclge_enable_ncsi_error(hdev, en);
-       if (ret)
-               dev_err(dev, "fail(%d) to en/disable err int\n", ret);
+       ret = hclge_config_ncsi_hw_err_int(hdev, en);
 
        return ret;
 }
 
-static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
+static int hclge_config_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
                                            bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc[2];
        int ret;
 
-       /* enable/disable PPP error interrupts */
+       /* configure PPP error interrupts */
        hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
        desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
        hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
@@ -567,24 +524,24 @@ static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT0_EN);
                        desc[0].data[1] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT1_EN);
-               } else {
-                       desc[0].data[0] = 0;
-                       desc[0].data[1] = 0;
+                       desc[0].data[4] = cpu_to_le32(HCLGE_PPP_PF_ERR_INT_EN);
                }
+
                desc[1].data[0] =
                        cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT0_EN_MASK);
                desc[1].data[1] =
                        cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT1_EN_MASK);
+               if (hdev->pdev->revision >= 0x21)
+                       desc[1].data[2] =
+                               cpu_to_le32(HCLGE_PPP_PF_ERR_INT_EN_MASK);
        } else if (cmd == HCLGE_PPP_CMD1_INT_CMD) {
                if (en) {
                        desc[0].data[0] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT2_EN);
                        desc[0].data[1] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT3_EN);
-               } else {
-                       desc[0].data[0] = 0;
-                       desc[0].data[1] = 0;
                }
+
                desc[1].data[0] =
                                cpu_to_le32(HCLGE_PPP_MPF_ECC_ERR_INT2_EN_MASK);
                desc[1].data[1] =
@@ -593,498 +550,863 @@ static int hclge_enable_ppp_error_interrupt(struct hclge_dev *hdev, u32 cmd,
 
        ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret)
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error interrupts\n",
-                       ret);
+               dev_err(dev, "fail(%d) to configure PPP error intr\n", ret);
 
        return ret;
 }
 
-static int hclge_enable_ppp_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_ppp_hw_err_int(struct hclge_dev *hdev, bool en)
 {
-       struct device *dev = &hdev->pdev->dev;
        int ret;
 
-       ret = hclge_enable_ppp_error_interrupt(hdev, HCLGE_PPP_CMD0_INT_CMD,
+       ret = hclge_config_ppp_error_interrupt(hdev, HCLGE_PPP_CMD0_INT_CMD,
                                               en);
-       if (ret) {
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error intr 0,1\n",
-                       ret);
+       if (ret)
                return ret;
-       }
 
-       ret = hclge_enable_ppp_error_interrupt(hdev, HCLGE_PPP_CMD1_INT_CMD,
+       ret = hclge_config_ppp_error_interrupt(hdev, HCLGE_PPP_CMD1_INT_CMD,
                                               en);
-       if (ret)
-               dev_err(dev,
-                       "failed(%d) to enable/disable PPP error intr 2,3\n",
-                       ret);
 
        return ret;
 }
 
-int hclge_enable_tm_hw_error(struct hclge_dev *hdev, bool en)
+static int hclge_config_tm_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* enable TM SCH hw errors */
+       /* configure TM SCH hw errors */
        hclge_cmd_setup_basic_desc(&desc, HCLGE_TM_SCH_ECC_INT_EN, false);
        if (en)
                desc.data[0] = cpu_to_le32(HCLGE_TM_SCH_ECC_ERR_INT_EN);
-       else
-               desc.data[0] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to configure TM SCH errors\n", ret);
+               dev_err(dev, "fail(%d) to configure TM SCH errors\n", ret);
                return ret;
        }
 
-       /* enable TM QCN hw errors */
+       /* configure TM QCN hw errors */
        ret = hclge_cmd_query_error(hdev, &desc, HCLGE_TM_QCN_MEM_INT_CFG,
                                    0, 0, 0);
        if (ret) {
-               dev_err(dev, "failed(%d) to read TM QCN CFG status\n", ret);
+               dev_err(dev, "fail(%d) to read TM QCN CFG status\n", ret);
                return ret;
        }
 
        hclge_cmd_reuse_desc(&desc, false);
        if (en)
                desc.data[1] = cpu_to_le32(HCLGE_TM_QCN_MEM_ERR_INT_EN);
-       else
-               desc.data[1] = 0;
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
                dev_err(dev,
-                       "failed(%d) to configure TM QCN mem errors\n", ret);
+                       "fail(%d) to configure TM QCN mem errors\n", ret);
 
        return ret;
 }
 
-static void hclge_process_common_error(struct hclge_dev *hdev,
-                                      enum hclge_err_int_type type)
+static int hclge_config_mac_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc[2];
-       u32 err_sts;
+       struct hclge_desc desc;
        int ret;
 
-       /* read err sts */
-       ret = hclge_cmd_query_error(hdev, &desc[0],
-                                   HCLGE_COMMON_ECC_INT_CFG,
-                                   HCLGE_CMD_FLAG_NEXT, 0, 0);
-       if (ret) {
+       /* configure MAC common error interrupts */
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_MAC_COMMON_INT_EN, false);
+       if (en)
+               desc.data[0] = cpu_to_le32(HCLGE_MAC_COMMON_ERR_INT_EN);
+
+       desc.data[1] = cpu_to_le32(HCLGE_MAC_COMMON_ERR_INT_EN_MASK);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
                dev_err(dev,
-                       "failed(=%d) to query COMMON error interrupt status\n",
-                       ret);
-               return;
-       }
+                       "fail(%d) to configure MAC COMMON error intr\n", ret);
 
-       /* log err */
-       err_sts = (le32_to_cpu(desc[0].data[0])) & HCLGE_IMP_TCM_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_imp_tcm_ecc_int[0], err_sts);
+       return ret;
+}
 
-       err_sts = (le32_to_cpu(desc[0].data[1])) & HCLGE_CMDQ_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_cmdq_nic_mem_ecc_int[0], err_sts);
+static int hclge_config_ppu_error_interrupts(struct hclge_dev *hdev, u32 cmd,
+                                            bool en)
+{
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       int num = 1;
+       int ret;
 
-       err_sts = (le32_to_cpu(desc[0].data[1]) >> HCLGE_CMDQ_ROC_ECC_INT_SHIFT)
-                  & HCLGE_CMDQ_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_cmdq_rocee_mem_ecc_int[0], err_sts);
+       /* configure PPU error interrupts */
+       if (cmd == HCLGE_PPU_MPF_ECC_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               desc[0].flag |= HCLGE_CMD_FLAG_NEXT;
+               hclge_cmd_setup_basic_desc(&desc[1], cmd, false);
+               if (en) {
+                       desc[0].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT0_EN;
+                       desc[0].data[1] = HCLGE_PPU_MPF_ABNORMAL_INT1_EN;
+                       desc[1].data[3] = HCLGE_PPU_MPF_ABNORMAL_INT3_EN;
+                       desc[1].data[4] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN;
+               }
 
-       if ((le32_to_cpu(desc[0].data[3])) & BIT(0))
-               dev_warn(dev, "imp_rd_data_poison_err found\n");
+               desc[1].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT0_EN_MASK;
+               desc[1].data[1] = HCLGE_PPU_MPF_ABNORMAL_INT1_EN_MASK;
+               desc[1].data[2] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN_MASK;
+               desc[1].data[3] |= HCLGE_PPU_MPF_ABNORMAL_INT3_EN_MASK;
+               num = 2;
+       } else if (cmd == HCLGE_PPU_MPF_OTHER_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               if (en)
+                       desc[0].data[0] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN2;
 
-       err_sts = (le32_to_cpu(desc[0].data[3]) >> HCLGE_TQP_ECC_INT_SHIFT) &
-                  HCLGE_TQP_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_tqp_int_ecc_int[0], err_sts);
+               desc[0].data[2] = HCLGE_PPU_MPF_ABNORMAL_INT2_EN2_MASK;
+       } else if (cmd == HCLGE_PPU_PF_OTHER_INT_CMD) {
+               hclge_cmd_setup_basic_desc(&desc[0], cmd, false);
+               if (en)
+                       desc[0].data[0] = HCLGE_PPU_PF_ABNORMAL_INT_EN;
 
-       err_sts = (le32_to_cpu(desc[0].data[5])) &
-                  HCLGE_IMP_ITCM4_ECC_INT_MASK;
-       hclge_log_error(dev, &hclge_imp_itcm4_ecc_int[0], err_sts);
+               desc[0].data[2] = HCLGE_PPU_PF_ABNORMAL_INT_EN_MASK;
+       } else {
+               dev_err(dev, "Invalid cmd to configure PPU error interrupts\n");
+               return -EINVAL;
+       }
 
-       /* clear error interrupts */
-       desc[1].data[0] = cpu_to_le32(HCLGE_IMP_TCM_ECC_CLR_MASK);
-       desc[1].data[1] = cpu_to_le32(HCLGE_CMDQ_NIC_ECC_CLR_MASK |
-                               HCLGE_CMDQ_ROCEE_ECC_CLR_MASK);
-       desc[1].data[3] = cpu_to_le32(HCLGE_TQP_IMP_ERR_CLR_MASK);
-       desc[1].data[5] = cpu_to_le32(HCLGE_IMP_ITCM4_ECC_CLR_MASK);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
 
-       ret = hclge_cmd_clear_error(hdev, &desc[0], NULL, 0,
-                                   HCLGE_CMD_FLAG_NEXT);
-       if (ret)
-               dev_err(dev,
-                       "failed(%d) to clear COMMON error interrupt status\n",
-                       ret);
+       return ret;
 }
 
-static void hclge_process_ncsi_error(struct hclge_dev *hdev,
-                                    enum hclge_err_int_type type)
+static int hclge_config_ppu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc_rd;
-       struct hclge_desc desc_wr;
-       u32 err_sts;
        int ret;
 
-       if (hdev->pdev->revision < 0x21)
-               return;
-
-       /* read NCSI error status */
-       ret = hclge_cmd_query_error(hdev, &desc_rd, HCLGE_NCSI_INT_QUERY,
-                                   0, 1, HCLGE_NCSI_ERR_INT_TYPE);
+       ret = hclge_config_ppu_error_interrupts(hdev, HCLGE_PPU_MPF_ECC_INT_CMD,
+                                               en);
        if (ret) {
-               dev_err(dev,
-                       "failed(=%d) to query NCSI error interrupt status\n",
+               dev_err(dev, "fail(%d) to configure PPU MPF ECC error intr\n",
                        ret);
-               return;
+               return ret;
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]);
-       hclge_log_error(dev, &hclge_ncsi_err_int[0], err_sts);
+       ret = hclge_config_ppu_error_interrupts(hdev,
+                                               HCLGE_PPU_MPF_OTHER_INT_CMD,
+                                               en);
+       if (ret) {
+               dev_err(dev, "fail(%d) to configure PPU MPF other intr\n", ret);
+               return ret;
+       }
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_NCSI_INT_CLR, 0);
+       ret = hclge_config_ppu_error_interrupts(hdev,
+                                               HCLGE_PPU_PF_OTHER_INT_CMD, en);
        if (ret)
-               dev_err(dev, "failed(=%d) to clear NCSI interrupt status\n",
+               dev_err(dev, "fail(%d) to configure PPU PF error interrupts\n",
                        ret);
+       return ret;
 }
 
-static void hclge_process_igu_egu_error(struct hclge_dev *hdev,
-                                       enum hclge_err_int_type int_type)
+static int hclge_config_ssu_hw_err_int(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
-       struct hclge_desc desc_rd;
-       struct hclge_desc desc_wr;
-       u32 err_sts;
+       struct hclge_desc desc[2];
        int ret;
 
-       /* read IGU common err sts */
-       ret = hclge_cmd_query_error(hdev, &desc_rd,
-                                   HCLGE_IGU_COMMON_INT_QUERY,
-                                   0, 1, int_type);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to query IGU common int status\n",
-                       ret);
-               return;
+       /* configure SSU ecc error interrupts */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_SSU_ECC_INT_CMD, false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_SSU_ECC_INT_CMD, false);
+       if (en) {
+               desc[0].data[0] = cpu_to_le32(HCLGE_SSU_1BIT_ECC_ERR_INT_EN);
+               desc[0].data[1] =
+                       cpu_to_le32(HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN);
+               desc[0].data[4] = cpu_to_le32(HCLGE_SSU_BIT32_ECC_ERR_INT_EN);
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]) &
-                                  HCLGE_IGU_COM_INT_MASK;
-       hclge_log_error(dev, &hclge_igu_com_err_int[0], err_sts);
+       desc[1].data[0] = cpu_to_le32(HCLGE_SSU_1BIT_ECC_ERR_INT_EN_MASK);
+       desc[1].data[1] = cpu_to_le32(HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK);
+       desc[1].data[2] = cpu_to_le32(HCLGE_SSU_BIT32_ECC_ERR_INT_EN_MASK);
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_IGU_COMMON_INT_CLR, 0);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
        if (ret) {
-               dev_err(dev, "failed(=%d) to clear IGU common int status\n",
-                       ret);
-               return;
+               dev_err(dev,
+                       "fail(%d) to configure SSU ECC error interrupt\n", ret);
+               return ret;
        }
 
-       /* read IGU-EGU TNL err sts */
-       ret = hclge_cmd_query_error(hdev, &desc_rd,
-                                   HCLGE_IGU_EGU_TNL_INT_QUERY,
-                                   0, 1, int_type);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to query IGU-EGU TNL int status\n",
-                       ret);
-               return;
+       /* configure SSU common error interrupts */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_SSU_COMMON_INT_CMD, false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       hclge_cmd_setup_basic_desc(&desc[1], HCLGE_SSU_COMMON_INT_CMD, false);
+
+       if (en) {
+               if (hdev->pdev->revision >= 0x21)
+                       desc[0].data[0] =
+                               cpu_to_le32(HCLGE_SSU_COMMON_INT_EN);
+               else
+                       desc[0].data[0] =
+                               cpu_to_le32(HCLGE_SSU_COMMON_INT_EN & ~BIT(5));
+               desc[0].data[1] = cpu_to_le32(HCLGE_SSU_PORT_BASED_ERR_INT_EN);
+               desc[0].data[2] =
+                       cpu_to_le32(HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN);
        }
 
-       /* log err */
-       err_sts = le32_to_cpu(desc_rd.data[0]) &
-                                  HCLGE_IGU_EGU_TNL_INT_MASK;
-       hclge_log_error(dev, &hclge_igu_egu_tnl_err_int[0], err_sts);
+       desc[1].data[0] = cpu_to_le32(HCLGE_SSU_COMMON_INT_EN_MASK |
+                               HCLGE_SSU_PORT_BASED_ERR_INT_EN_MASK);
+       desc[1].data[1] = cpu_to_le32(HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK);
 
-       /* clear err int */
-       ret = hclge_cmd_clear_error(hdev, &desc_wr, &desc_rd,
-                                   HCLGE_IGU_EGU_TNL_INT_CLR, 0);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to clear IGU-EGU TNL int status\n",
-                       ret);
-               return;
-       }
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 2);
+       if (ret)
+               dev_err(dev,
+                       "fail(%d) to configure SSU COMMON error intr\n", ret);
 
-       hclge_process_ncsi_error(hdev, HCLGE_ERR_INT_RAS_NFE);
+       return ret;
 }
 
-static int hclge_log_and_clear_ppp_error(struct hclge_dev *hdev, u32 cmd,
-                                        enum hclge_err_int_type int_type)
+#define HCLGE_SET_DEFAULT_RESET_REQUEST(reset_type) \
+       do { \
+               if (ae_dev->ops->set_default_reset_request) \
+                       ae_dev->ops->set_default_reset_request(ae_dev, \
+                                                              reset_type); \
+       } while (0)
+
+/* hclge_handle_mpf_ras_error: handle all main PF RAS errors
+ * @hdev: pointer to struct hclge_dev
+ * @desc: descriptor for describing the command
+ * @num:  number of extended command structures
+ *
+ * This function handles all the main PF RAS errors in the
+ * hw register/s using command.
+ */
+static int hclge_handle_mpf_ras_error(struct hclge_dev *hdev,
+                                     struct hclge_desc *desc,
+                                     int num)
 {
-       enum hnae3_reset_type reset_level = HNAE3_NONE_RESET;
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
        struct device *dev = &hdev->pdev->dev;
-       const struct hclge_hw_error *hw_err_lst1, *hw_err_lst2, *hw_err_lst3;
-       struct hclge_desc desc[2];
-       u32 err_sts;
+       __le32 *desc_data;
+       u32 status;
        int ret;
 
-       /* read PPP INT sts */
-       ret = hclge_cmd_query_error(hdev, &desc[0], cmd,
-                                   HCLGE_CMD_FLAG_NEXT, 5, int_type);
+       /* query all main PF RAS errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_MPF_RAS_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
        if (ret) {
-               dev_err(dev, "failed(=%d) to query PPP interrupt status\n",
-                       ret);
-               return -EIO;
+               dev_err(dev, "query all mpf ras int cmd failed (%d)\n", ret);
+               return ret;
        }
 
-       /* log error */
-       if (cmd == HCLGE_PPP_CMD0_INT_CMD) {
-               hw_err_lst1 = &hclge_ppp_mpf_int0[0];
-               hw_err_lst2 = &hclge_ppp_mpf_int1[0];
-               hw_err_lst3 = &hclge_ppp_pf_int[0];
-       } else if (cmd == HCLGE_PPP_CMD1_INT_CMD) {
-               hw_err_lst1 = &hclge_ppp_mpf_int2[0];
-               hw_err_lst2 = &hclge_ppp_mpf_int3[0];
-       } else {
-               dev_err(dev, "invalid command(=%d)\n", cmd);
-               return -EINVAL;
+       /* log HNS common errors */
+       status = le32_to_cpu(desc[0].data[0]);
+       if (status) {
+               hclge_log_error(dev, "IMP_TCM_ECC_INT_STS",
+                               &hclge_imp_tcm_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
        }
 
-       err_sts = le32_to_cpu(desc[0].data[2]);
-       if (err_sts) {
-               hclge_log_error(dev, hw_err_lst1, err_sts);
-               reset_level = HNAE3_FUNC_RESET;
+       status = le32_to_cpu(desc[0].data[1]);
+       if (status) {
+               hclge_log_error(dev, "CMDQ_MEM_ECC_INT_STS",
+                               &hclge_cmdq_nic_mem_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
        }
 
-       err_sts = le32_to_cpu(desc[0].data[3]);
-       if (err_sts) {
-               hclge_log_error(dev, hw_err_lst2, err_sts);
-               reset_level = HNAE3_FUNC_RESET;
+       if ((le32_to_cpu(desc[0].data[2])) & BIT(0)) {
+               dev_warn(dev, "imp_rd_data_poison_err found\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
        }
 
-       if (cmd == HCLGE_PPP_CMD0_INT_CMD) {
-               err_sts = (le32_to_cpu(desc[0].data[4]) >> 8) & 0x3;
-               if (err_sts) {
-                       hclge_log_error(dev, hw_err_lst3, err_sts);
-                       reset_level = HNAE3_FUNC_RESET;
-               }
+       status = le32_to_cpu(desc[0].data[3]);
+       if (status) {
+               hclge_log_error(dev, "TQP_INT_ECC_INT_STS",
+                               &hclge_tqp_int_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
        }
 
-       /* clear PPP INT */
-       ret = hclge_cmd_clear_error(hdev, &desc[0], NULL, 0,
-                                   HCLGE_CMD_FLAG_NEXT);
-       if (ret) {
-               dev_err(dev, "failed(=%d) to clear PPP interrupt status\n",
-                       ret);
-               return -EIO;
+       status = le32_to_cpu(desc[0].data[4]);
+       if (status) {
+               hclge_log_error(dev, "MSIX_ECC_INT_STS",
+                               &hclge_msix_sram_ecc_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
        }
 
-       return 0;
+       /* log SSU(Storage Switch Unit) errors */
+       desc_data = (__le32 *)&desc[2];
+       status = le32_to_cpu(*(desc_data + 2));
+       if (status) {
+               dev_warn(dev, "SSU_ECC_MULTI_BIT_INT_0 ssu_ecc_mbit_int[31:0]\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 3)) & BIT(0);
+       if (status) {
+               dev_warn(dev, "SSU_ECC_MULTI_BIT_INT_1 ssu_ecc_mbit_int[32]\n");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 4)) & HCLGE_SSU_COMMON_ERR_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "SSU_COMMON_ERR_INT",
+                               &hclge_ssu_com_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       /* log IGU(Ingress Unit) errors */
+       desc_data = (__le32 *)&desc[3];
+       status = le32_to_cpu(*desc_data) & HCLGE_IGU_INT_MASK;
+       if (status)
+               hclge_log_error(dev, "IGU_INT_STS",
+                               &hclge_igu_int[0], status);
+
+       /* log PPP(Programmable Packet Process) errors */
+       desc_data = (__le32 *)&desc[4];
+       status = le32_to_cpu(*(desc_data + 1));
+       if (status)
+               hclge_log_error(dev, "PPP_MPF_ABNORMAL_INT_ST1",
+                               &hclge_ppp_mpf_abnormal_int_st1[0], status);
+
+       status = le32_to_cpu(*(desc_data + 3)) & HCLGE_PPP_MPF_INT_ST3_MASK;
+       if (status)
+               hclge_log_error(dev, "PPP_MPF_ABNORMAL_INT_ST3",
+                               &hclge_ppp_mpf_abnormal_int_st3[0], status);
+
+       /* log PPU(RCB) errors */
+       desc_data = (__le32 *)&desc[5];
+       status = le32_to_cpu(*(desc_data + 1));
+       if (status) {
+               dev_warn(dev, "PPU_MPF_ABNORMAL_INT_ST1 %s found\n",
+                        "rpu_rx_pkt_ecc_mbit_err");
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 2));
+       if (status) {
+               hclge_log_error(dev, "PPU_MPF_ABNORMAL_INT_ST2",
+                               &hclge_ppu_mpf_abnormal_int_st2[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 3)) & HCLGE_PPU_MPF_INT_ST3_MASK;
+       if (status) {
+               hclge_log_error(dev, "PPU_MPF_ABNORMAL_INT_ST3",
+                               &hclge_ppu_mpf_abnormal_int_st3[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log TM(Traffic Manager) errors */
+       desc_data = (__le32 *)&desc[6];
+       status = le32_to_cpu(*desc_data);
+       if (status) {
+               hclge_log_error(dev, "TM_SCH_RINT",
+                               &hclge_tm_sch_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log QCN(Quantized Congestion Control) errors */
+       desc_data = (__le32 *)&desc[7];
+       status = le32_to_cpu(*desc_data) & HCLGE_QCN_FIFO_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "QCN_FIFO_RINT",
+                               &hclge_qcn_fifo_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       status = le32_to_cpu(*(desc_data + 1)) & HCLGE_QCN_ECC_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "QCN_ECC_RINT",
+                               &hclge_qcn_ecc_rint[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* log NCSI errors */
+       desc_data = (__le32 *)&desc[9];
+       status = le32_to_cpu(*desc_data) & HCLGE_NCSI_ECC_INT_MASK;
+       if (status) {
+               hclge_log_error(dev, "NCSI_ECC_INT_RPT",
+                               &hclge_ncsi_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_CORE_RESET);
+       }
+
+       /* clear all main PF RAS errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret)
+               dev_err(dev, "clear all mpf ras int cmd failed (%d)\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_ppp_error(struct hclge_dev *hdev,
-                                   enum hclge_err_int_type int_type)
+/* hclge_handle_pf_ras_error: handle all PF RAS errors
+ * @hdev: pointer to struct hclge_dev
+ * @desc: descriptor for describing the command
+ * @num:  number of extended command structures
+ *
+ * This function handles all the PF RAS errors in the
+ * hw register/s using command.
+ */
+static int hclge_handle_pf_ras_error(struct hclge_dev *hdev,
+                                    struct hclge_desc *desc,
+                                    int num)
 {
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
        struct device *dev = &hdev->pdev->dev;
+       __le32 *desc_data;
+       u32 status;
        int ret;
 
-       /* read PPP INT0,1 sts */
-       ret = hclge_log_and_clear_ppp_error(hdev, HCLGE_PPP_CMD0_INT_CMD,
-                                           int_type);
-       if (ret < 0) {
-               dev_err(dev, "failed(=%d) to clear PPP interrupt 0,1 status\n",
-                       ret);
-               return;
+       /* query all PF RAS errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_PF_RAS_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret) {
+               dev_err(dev, "query all pf ras int cmd failed (%d)\n", ret);
+               return ret;
        }
 
-       /* read err PPP INT2,3 sts */
-       ret = hclge_log_and_clear_ppp_error(hdev, HCLGE_PPP_CMD1_INT_CMD,
-                                           int_type);
-       if (ret < 0)
-               dev_err(dev, "failed(=%d) to clear PPP interrupt 2,3 status\n",
-                       ret);
+       /* log SSU(Storage Switch Unit) errors */
+       status = le32_to_cpu(desc[0].data[0]);
+       if (status) {
+               hclge_log_error(dev, "SSU_PORT_BASED_ERR_INT",
+                               &hclge_ssu_port_based_err_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       status = le32_to_cpu(desc[0].data[1]);
+       if (status) {
+               hclge_log_error(dev, "SSU_FIFO_OVERFLOW_INT",
+                               &hclge_ssu_fifo_overflow_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       status = le32_to_cpu(desc[0].data[2]);
+       if (status) {
+               hclge_log_error(dev, "SSU_ETS_TCG_INT",
+                               &hclge_ssu_ets_tcg_int[0], status);
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+       }
+
+       /* log IGU(Ingress Unit) EGU(Egress Unit) TNL errors */
+       desc_data = (__le32 *)&desc[1];
+       status = le32_to_cpu(*desc_data) & HCLGE_IGU_EGU_TNL_INT_MASK;
+       if (status)
+               hclge_log_error(dev, "IGU_EGU_TNL_INT_STS",
+                               &hclge_igu_egu_tnl_int[0], status);
+
+       /* clear all PF RAS errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], num);
+       if (ret)
+               dev_err(dev, "clear all pf ras int cmd failed (%d)\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_tm_sch_error(struct hclge_dev *hdev)
+static int hclge_handle_all_ras_errors(struct hclge_dev *hdev)
 {
        struct device *dev = &hdev->pdev->dev;
-       const struct hclge_tm_sch_ecc_info *tm_sch_ecc_info;
-       struct hclge_desc desc;
-       u32 ecc_info;
-       u8 module_no;
-       u8 ram_no;
+       u32 mpf_bd_num, pf_bd_num, bd_num;
+       struct hclge_desc desc_bd;
+       struct hclge_desc *desc;
        int ret;
 
-       /* read TM scheduler errors */
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_MBIT_ECC_INFO_CMD, 0, 0, 0);
+       /* query the number of registers in the RAS int status */
+       hclge_cmd_setup_basic_desc(&desc_bd, HCLGE_QUERY_RAS_INT_STS_BD_NUM,
+                                  true);
+       ret = hclge_cmd_send(&hdev->hw, &desc_bd, 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH mbit ECC err info\n", ret);
-               return;
+               dev_err(dev, "fail(%d) to query ras int status bd num\n", ret);
+               return ret;
        }
-       ecc_info = le32_to_cpu(desc.data[0]);
+       mpf_bd_num = le32_to_cpu(desc_bd.data[0]);
+       pf_bd_num = le32_to_cpu(desc_bd.data[1]);
+       bd_num = max_t(u32, mpf_bd_num, pf_bd_num);
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_CMD, 0, 0, 0);
+       desc = kcalloc(bd_num, sizeof(struct hclge_desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+
+       /* handle all main PF RAS errors */
+       ret = hclge_handle_mpf_ras_error(hdev, desc, mpf_bd_num);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH ECC err status\n", ret);
-               return;
+               kfree(desc);
+               return ret;
        }
+       memset(desc, 0, bd_num * sizeof(struct hclge_desc));
+
+       /* handle all PF RAS errors */
+       ret = hclge_handle_pf_ras_error(hdev, desc, pf_bd_num);
+       kfree(desc);
 
-       /* log TM scheduler errors */
-       if (le32_to_cpu(desc.data[0])) {
-               hclge_log_error(dev, &hclge_tm_sch_err_int[0],
-                               le32_to_cpu(desc.data[0]));
-               if (le32_to_cpu(desc.data[0]) & 0x2) {
-                       module_no = (ecc_info >> 20) & 0xF;
-                       ram_no = (ecc_info >> 16) & 0xF;
-                       tm_sch_ecc_info =
-                               &hclge_tm_sch_ecc_err[module_no][ram_no];
-                       dev_warn(dev, "ecc err module:ram=%s\n",
-                                tm_sch_ecc_info->name);
-                       dev_warn(dev, "ecc memory address = 0x%x\n",
-                                ecc_info & 0xFFFF);
+       return ret;
+}
+
+static int hclge_log_rocee_ovf_error(struct hclge_dev *hdev)
+{
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       int ret;
+
+       /* read overflow error status */
+       ret = hclge_cmd_query_error(hdev, &desc[0],
+                                   HCLGE_ROCEE_PF_RAS_INT_CMD,
+                                   0, 0, 0);
+       if (ret) {
+               dev_err(dev, "failed(%d) to query ROCEE OVF error sts\n", ret);
+               return ret;
+       }
+
+       /* log overflow error */
+       if (le32_to_cpu(desc[0].data[0]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               const struct hclge_hw_error *err;
+               u32 err_sts;
+
+               err = &hclge_rocee_qmm_ovf_err_int[0];
+               err_sts = HCLGE_ROCEE_OVF_ERR_TYPE_MASK &
+                         le32_to_cpu(desc[0].data[0]);
+               while (err->msg) {
+                       if (err->int_msk == err_sts) {
+                               dev_warn(dev, "%s [error status=0x%x] found\n",
+                                        err->msg,
+                                        le32_to_cpu(desc[0].data[0]));
+                               break;
+                       }
+                       err++;
                }
        }
 
-       /* clear TM scheduler errors */
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH error status\n", ret);
-               return;
+       if (le32_to_cpu(desc[0].data[1]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               dev_warn(dev, "ROCEE TSP OVF [error status=0x%x] found\n",
+                        le32_to_cpu(desc[0].data[1]));
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_CE, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read SCH CE status\n", ret);
-               return;
+       if (le32_to_cpu(desc[0].data[2]) & HCLGE_ROCEE_OVF_ERR_INT_MASK) {
+               dev_warn(dev, "ROCEE SCC OVF [error status=0x%x] found\n",
+                        le32_to_cpu(desc[0].data[2]));
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
+       return 0;
+}
+
+static int hclge_log_and_clear_rocee_ras_error(struct hclge_dev *hdev)
+{
+       enum hnae3_reset_type reset_type = HNAE3_FUNC_RESET;
+       struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
+       struct device *dev = &hdev->pdev->dev;
+       struct hclge_desc desc[2];
+       unsigned int status;
+       int ret;
+
+       /* read RAS error interrupt status */
+       ret = hclge_cmd_query_error(hdev, &desc[0],
+                                   HCLGE_QUERY_CLEAR_ROCEE_RAS_INT,
+                                   0, 0, 0);
        if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH CE status\n", ret);
-               return;
+               dev_err(dev, "failed(%d) to query ROCEE RAS INT SRC\n", ret);
+               /* reset everything for now */
+               HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+               return ret;
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_NFE, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read SCH NFE status\n", ret);
-               return;
+       status = le32_to_cpu(desc[0].data[0]);
+
+       if (status & HCLGE_ROCEE_RERR_INT_MASK)
+               dev_warn(dev, "ROCEE RAS AXI rresp error\n");
+
+       if (status & HCLGE_ROCEE_BERR_INT_MASK)
+               dev_warn(dev, "ROCEE RAS AXI bresp error\n");
+
+       if (status & HCLGE_ROCEE_ECC_INT_MASK) {
+               dev_warn(dev, "ROCEE RAS 2bit ECC error\n");
+               reset_type = HNAE3_GLOBAL_RESET;
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to clear TM SCH NFE status\n", ret);
-               return;
+       if (status & HCLGE_ROCEE_OVF_INT_MASK) {
+               ret = hclge_log_rocee_ovf_error(hdev);
+               if (ret) {
+                       dev_err(dev, "failed(%d) to process ovf error\n", ret);
+                       /* reset everything for now */
+                       HCLGE_SET_DEFAULT_RESET_REQUEST(HNAE3_GLOBAL_RESET);
+                       return ret;
+               }
        }
 
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_SCH_ECC_ERR_RINT_FE, 0, 0, 0);
+       /* clear error status */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], 1);
        if (ret) {
-               dev_err(dev, "failed(%d) to read SCH FE status\n", ret);
-               return;
+               dev_err(dev, "failed(%d) to clear ROCEE RAS error\n", ret);
+               /* reset everything for now */
+               reset_type = HNAE3_GLOBAL_RESET;
        }
 
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
-       if (ret)
-               dev_err(dev, "failed(%d) to clear TM SCH FE status\n", ret);
+       HCLGE_SET_DEFAULT_RESET_REQUEST(reset_type);
+
+       return ret;
 }
 
-static void hclge_process_tm_qcn_error(struct hclge_dev *hdev)
+static int hclge_config_rocee_ras_interrupt(struct hclge_dev *hdev, bool en)
 {
        struct device *dev = &hdev->pdev->dev;
        struct hclge_desc desc;
        int ret;
 
-       /* read QCN errors */
-       ret = hclge_cmd_query_error(hdev, &desc,
-                                   HCLGE_TM_QCN_MEM_INT_INFO_CMD, 0, 0, 0);
-       if (ret) {
-               dev_err(dev, "failed(%d) to read QCN ECC err status\n", ret);
-               return;
-       }
+       if (hdev->pdev->revision < 0x21 || !hnae3_dev_roce_supported(hdev))
+               return 0;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_CONFIG_ROCEE_RAS_INT_EN, false);
+       if (en) {
+               /* enable ROCEE hw error interrupts */
+               desc.data[0] = cpu_to_le32(HCLGE_ROCEE_RAS_NFE_INT_EN);
+               desc.data[1] = cpu_to_le32(HCLGE_ROCEE_RAS_CE_INT_EN);
 
-       /* log QCN errors */
-       if (le32_to_cpu(desc.data[0]))
-               hclge_log_error(dev, &hclge_qcn_ecc_err_int[0],
-                               le32_to_cpu(desc.data[0]));
+               hclge_log_and_clear_rocee_ras_error(hdev);
+       }
+       desc.data[2] = cpu_to_le32(HCLGE_ROCEE_RAS_NFE_INT_EN_MASK);
+       desc.data[3] = cpu_to_le32(HCLGE_ROCEE_RAS_CE_INT_EN_MASK);
 
-       /* clear QCN errors */
-       ret = hclge_cmd_clear_error(hdev, &desc, NULL, 0, 0);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
-               dev_err(dev, "failed(%d) to clear QCN error status\n", ret);
+               dev_err(dev, "failed(%d) to config ROCEE RAS interrupt\n", ret);
+
+       return ret;
 }
 
-static void hclge_process_tm_error(struct hclge_dev *hdev,
-                                  enum hclge_err_int_type type)
+static int hclge_handle_rocee_ras_error(struct hnae3_ae_dev *ae_dev)
 {
-       hclge_process_tm_sch_error(hdev);
-       hclge_process_tm_qcn_error(hdev);
+       struct hclge_dev *hdev = ae_dev->priv;
+
+       if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
+           hdev->pdev->revision < 0x21)
+               return HNAE3_NONE_RESET;
+
+       return hclge_log_and_clear_rocee_ras_error(hdev);
 }
 
 static const struct hclge_hw_blk hw_blk[] = {
-       { .msk = BIT(0), .name = "IGU_EGU",
-         .enable_error = hclge_enable_igu_egu_error,
-         .process_error = hclge_process_igu_egu_error, },
-       { .msk = BIT(5), .name = "COMMON",
-         .enable_error = hclge_enable_common_error,
-         .process_error = hclge_process_common_error, },
-       { .msk = BIT(4), .name = "TM",
-         .enable_error = hclge_enable_tm_hw_error,
-         .process_error = hclge_process_tm_error, },
-       { .msk = BIT(1), .name = "PPP",
-         .enable_error = hclge_enable_ppp_error,
-         .process_error = hclge_process_ppp_error, },
+       {
+         .msk = BIT(0), .name = "IGU_EGU",
+         .config_err_int = hclge_config_igu_egu_hw_err_int,
+       },
+       {
+         .msk = BIT(1), .name = "PPP",
+         .config_err_int = hclge_config_ppp_hw_err_int,
+       },
+       {
+         .msk = BIT(2), .name = "SSU",
+         .config_err_int = hclge_config_ssu_hw_err_int,
+       },
+       {
+         .msk = BIT(3), .name = "PPU",
+         .config_err_int = hclge_config_ppu_hw_err_int,
+       },
+       {
+         .msk = BIT(4), .name = "TM",
+         .config_err_int = hclge_config_tm_hw_err_int,
+       },
+       {
+         .msk = BIT(5), .name = "COMMON",
+         .config_err_int = hclge_config_common_hw_err_int,
+       },
+       {
+         .msk = BIT(8), .name = "MAC",
+         .config_err_int = hclge_config_mac_err_int,
+       },
        { /* sentinel */ }
 };
 
 int hclge_hw_error_set_state(struct hclge_dev *hdev, bool state)
 {
+       const struct hclge_hw_blk *module = hw_blk;
        struct device *dev = &hdev->pdev->dev;
        int ret = 0;
-       int i = 0;
 
-       while (hw_blk[i].name) {
-               if (!hw_blk[i].enable_error) {
-                       i++;
-                       continue;
+       while (module->name) {
+               if (module->config_err_int) {
+                       ret = module->config_err_int(hdev, state);
+                       if (ret)
+                               return ret;
                }
-               ret = hw_blk[i].enable_error(hdev, state);
-               if (ret) {
-                       dev_err(dev, "fail(%d) to en/disable err int\n", ret);
-                       return ret;
-               }
-               i++;
+               module++;
        }
 
+       ret = hclge_config_rocee_ras_interrupt(hdev, state);
+       if (ret)
+               dev_err(dev, "fail(%d) to configure ROCEE err int\n", ret);
+
        return ret;
 }
 
-pci_ers_result_t hclge_process_ras_hw_error(struct hnae3_ae_dev *ae_dev)
+pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev)
 {
        struct hclge_dev *hdev = ae_dev->priv;
        struct device *dev = &hdev->pdev->dev;
-       u32 sts, val;
-       int i = 0;
-
-       sts = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG);
-
-       /* Processing Non-fatal errors */
-       if (sts & HCLGE_RAS_REG_NFE_MASK) {
-               val = (sts >> HCLGE_RAS_REG_NFE_SHIFT) & 0xFF;
-               i = 0;
-               while (hw_blk[i].name) {
-                       if (!(hw_blk[i].msk & val)) {
-                               i++;
-                               continue;
-                       }
-                       dev_warn(dev, "%s ras non-fatal error identified\n",
-                                hw_blk[i].name);
-                       if (hw_blk[i].process_error)
-                               hw_blk[i].process_error(hdev,
-                                                        HCLGE_ERR_INT_RAS_NFE);
-                       i++;
-               }
+       u32 status;
+
+       status = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG);
+
+       /* Handling Non-fatal HNS RAS errors */
+       if (status & HCLGE_RAS_REG_NFE_MASK) {
+               dev_warn(dev,
+                        "HNS Non-Fatal RAS error(status=0x%x) identified\n",
+                        status);
+               hclge_handle_all_ras_errors(hdev);
+       } else {
+               if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
+                   hdev->pdev->revision < 0x21)
+                       return PCI_ERS_RESULT_RECOVERED;
+       }
+
+       if (status & HCLGE_RAS_REG_ROCEE_ERR_MASK) {
+               dev_warn(dev, "ROCEE uncorrected RAS error identified\n");
+               hclge_handle_rocee_ras_error(ae_dev);
+       }
+
+       if (status & HCLGE_RAS_REG_NFE_MASK ||
+           status & HCLGE_RAS_REG_ROCEE_ERR_MASK)
+               return PCI_ERS_RESULT_NEED_RESET;
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+int hclge_handle_hw_msix_error(struct hclge_dev *hdev,
+                              unsigned long *reset_requests)
+{
+       struct device *dev = &hdev->pdev->dev;
+       u32 mpf_bd_num, pf_bd_num, bd_num;
+       struct hclge_desc desc_bd;
+       struct hclge_desc *desc;
+       __le32 *desc_data;
+       int ret = 0;
+       u32 status;
+
+       /* set default handling */
+       set_bit(HNAE3_FUNC_RESET, reset_requests);
+
+       /* query the number of bds for the MSIx int status */
+       hclge_cmd_setup_basic_desc(&desc_bd, HCLGE_QUERY_MSIX_INT_STS_BD_NUM,
+                                  true);
+       ret = hclge_cmd_send(&hdev->hw, &desc_bd, 1);
+       if (ret) {
+               dev_err(dev, "fail(%d) to query msix int status bd num\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               return ret;
+       }
+
+       mpf_bd_num = le32_to_cpu(desc_bd.data[0]);
+       pf_bd_num = le32_to_cpu(desc_bd.data[1]);
+       bd_num = max_t(u32, mpf_bd_num, pf_bd_num);
+
+       desc = kcalloc(bd_num, sizeof(struct hclge_desc), GFP_KERNEL);
+       if (!desc)
+               goto out;
+
+       /* query all main PF MSIx errors */
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], mpf_bd_num);
+       if (ret) {
+               dev_err(dev, "query all mpf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* log MAC errors */
+       desc_data = (__le32 *)&desc[1];
+       status = le32_to_cpu(*desc_data);
+       if (status) {
+               hclge_log_error(dev, "MAC_AFIFO_TNL_INT_R",
+                               &hclge_mac_afifo_tnl_int[0], status);
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
        }
 
-       return PCI_ERS_RESULT_NEED_RESET;
+       /* log PPU(RCB) errors */
+       desc_data = (__le32 *)&desc[5];
+       status = le32_to_cpu(*(desc_data + 2)) &
+                       HCLGE_PPU_MPF_INT_ST2_MSIX_MASK;
+       if (status) {
+               dev_warn(dev,
+                        "PPU_MPF_ABNORMAL_INT_ST2[28:29], err_status(0x%x)\n",
+                        status);
+               set_bit(HNAE3_CORE_RESET, reset_requests);
+       }
+
+       /* clear all main PF MSIx errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], mpf_bd_num);
+       if (ret) {
+               dev_err(dev, "clear all mpf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* query all PF MSIx errors */
+       memset(desc, 0, bd_num * sizeof(struct hclge_desc));
+       hclge_cmd_setup_basic_desc(&desc[0], HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT,
+                                  true);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], pf_bd_num);
+       if (ret) {
+               dev_err(dev, "query all pf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+               goto msi_error;
+       }
+
+       /* log SSU PF errors */
+       status = le32_to_cpu(desc[0].data[0]) & HCLGE_SSU_PORT_INT_MSIX_MASK;
+       if (status) {
+               hclge_log_error(dev, "SSU_PORT_BASED_ERR_INT",
+                               &hclge_ssu_port_based_pf_int[0], status);
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+       }
+
+       /* read and log PPP PF errors */
+       desc_data = (__le32 *)&desc[2];
+       status = le32_to_cpu(*desc_data);
+       if (status)
+               hclge_log_error(dev, "PPP_PF_ABNORMAL_INT_ST0",
+                               &hclge_ppp_pf_abnormal_int[0], status);
+
+       /* PPU(RCB) PF errors */
+       desc_data = (__le32 *)&desc[3];
+       status = le32_to_cpu(*desc_data) & HCLGE_PPU_PF_INT_MSIX_MASK;
+       if (status)
+               hclge_log_error(dev, "PPU_PF_ABNORMAL_INT_ST",
+                               &hclge_ppu_pf_abnormal_int[0], status);
+
+       /* clear all PF MSIx errors */
+       hclge_cmd_reuse_desc(&desc[0], false);
+       desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc[0], pf_bd_num);
+       if (ret) {
+               dev_err(dev, "clear all pf msix int cmd failed (%d)\n",
+                       ret);
+               /* reset everything for now */
+               set_bit(HNAE3_GLOBAL_RESET, reset_requests);
+       }
+
+msi_error:
+       kfree(desc);
+out:
+       return ret;
 }
index e0e3b58..51a7d4e 100644 (file)
@@ -7,9 +7,11 @@
 #include "hclge_main.h"
 
 #define HCLGE_RAS_PF_OTHER_INT_STS_REG   0x20B00
-#define HCLGE_RAS_REG_FE_MASK    0xFF
 #define HCLGE_RAS_REG_NFE_MASK   0xFF00
-#define HCLGE_RAS_REG_NFE_SHIFT        8
+#define HCLGE_RAS_REG_ROCEE_ERR_MASK   0x3000000
+
+#define HCLGE_VECTOR0_PF_OTHER_INT_STS_REG   0x20800
+#define HCLGE_VECTOR0_REG_MSIX_MASK   0x1FF00
 
 #define HCLGE_IMP_TCM_ECC_ERR_INT_EN   0xFFFF0000
 #define HCLGE_IMP_TCM_ECC_ERR_INT_EN_MASK      0xFFFF0000
@@ -23,6 +25,8 @@
 #define HCLGE_IMP_RD_POISON_ERR_INT_EN_MASK    0x0100
 #define HCLGE_TQP_ECC_ERR_INT_EN       0x0FFF
 #define HCLGE_TQP_ECC_ERR_INT_EN_MASK  0x0FFF
+#define HCLGE_MSIX_SRAM_ECC_ERR_INT_EN_MASK    0x0F000000
+#define HCLGE_MSIX_SRAM_ECC_ERR_INT_EN 0x0F000000
 #define HCLGE_IGU_ERR_INT_EN   0x0000066F
 #define HCLGE_IGU_ERR_INT_EN_MASK      0x000F
 #define HCLGE_IGU_TNL_ERR_INT_EN    0x0002AABF
 #define HCLGE_TM_QCN_MEM_ERR_INT_EN    0xFFFFFF
 #define HCLGE_NCSI_ERR_INT_EN  0x3
 #define HCLGE_NCSI_ERR_INT_TYPE        0x9
+#define HCLGE_MAC_COMMON_ERR_INT_EN            GENMASK(7, 0)
+#define HCLGE_MAC_COMMON_ERR_INT_EN_MASK       GENMASK(7, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT0_EN         GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT0_EN_MASK    GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT1_EN         GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT1_EN_MASK    GENMASK(31, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN         0x3FFF3FFF
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN_MASK    0x3FFF3FFF
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN2                0xB
+#define HCLGE_PPU_MPF_ABNORMAL_INT2_EN2_MASK   0xB
+#define HCLGE_PPU_MPF_ABNORMAL_INT3_EN         GENMASK(7, 0)
+#define HCLGE_PPU_MPF_ABNORMAL_INT3_EN_MASK    GENMASK(23, 16)
+#define HCLGE_PPU_PF_ABNORMAL_INT_EN           GENMASK(5, 0)
+#define HCLGE_PPU_PF_ABNORMAL_INT_EN_MASK      GENMASK(5, 0)
+#define HCLGE_SSU_1BIT_ECC_ERR_INT_EN          GENMASK(31, 0)
+#define HCLGE_SSU_1BIT_ECC_ERR_INT_EN_MASK     GENMASK(31, 0)
+#define HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN     GENMASK(31, 0)
+#define HCLGE_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK        GENMASK(31, 0)
+#define HCLGE_SSU_BIT32_ECC_ERR_INT_EN         0x0101
+#define HCLGE_SSU_BIT32_ECC_ERR_INT_EN_MASK    0x0101
+#define HCLGE_SSU_COMMON_INT_EN                        GENMASK(9, 0)
+#define HCLGE_SSU_COMMON_INT_EN_MASK           GENMASK(9, 0)
+#define HCLGE_SSU_PORT_BASED_ERR_INT_EN                0x0BFF
+#define HCLGE_SSU_PORT_BASED_ERR_INT_EN_MASK   0x0BFF0000
+#define HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN     GENMASK(23, 0)
+#define HCLGE_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK        GENMASK(23, 0)
+
+#define HCLGE_SSU_COMMON_ERR_INT_MASK  GENMASK(9, 0)
+#define HCLGE_SSU_PORT_INT_MSIX_MASK   0x7BFF
+#define HCLGE_IGU_INT_MASK             GENMASK(3, 0)
+#define HCLGE_IGU_EGU_TNL_INT_MASK     GENMASK(5, 0)
+#define HCLGE_PPP_MPF_INT_ST3_MASK     GENMASK(5, 0)
+#define HCLGE_PPU_MPF_INT_ST3_MASK     GENMASK(7, 0)
+#define HCLGE_PPU_MPF_INT_ST2_MSIX_MASK        GENMASK(29, 28)
+#define HCLGE_PPU_PF_INT_MSIX_MASK     0x27
+#define HCLGE_QCN_FIFO_INT_MASK                GENMASK(17, 0)
+#define HCLGE_QCN_ECC_INT_MASK         GENMASK(21, 0)
+#define HCLGE_NCSI_ECC_INT_MASK                GENMASK(1, 0)
 
-#define HCLGE_IMP_TCM_ECC_INT_MASK     0xFFFF
-#define HCLGE_IMP_ITCM4_ECC_INT_MASK   0x3
-#define HCLGE_CMDQ_ECC_INT_MASK                0xFFFF
-#define HCLGE_CMDQ_ROC_ECC_INT_SHIFT   16
-#define HCLGE_TQP_ECC_INT_MASK         0xFFF
-#define HCLGE_TQP_ECC_INT_SHIFT                16
-#define HCLGE_IMP_TCM_ECC_CLR_MASK     0xFFFF
-#define HCLGE_IMP_ITCM4_ECC_CLR_MASK   0x3
-#define HCLGE_CMDQ_NIC_ECC_CLR_MASK    0xFFFF
-#define HCLGE_CMDQ_ROCEE_ECC_CLR_MASK  0xFFFF0000
-#define HCLGE_TQP_IMP_ERR_CLR_MASK     0x0FFF0001
-#define HCLGE_IGU_COM_INT_MASK         0xF
-#define HCLGE_IGU_EGU_TNL_INT_MASK     0x3F
-#define HCLGE_PPP_PF_INT_MASK          0x100
+#define HCLGE_ROCEE_RAS_NFE_INT_EN             0xF
+#define HCLGE_ROCEE_RAS_CE_INT_EN              0x1
+#define HCLGE_ROCEE_RAS_NFE_INT_EN_MASK                0xF
+#define HCLGE_ROCEE_RAS_CE_INT_EN_MASK         0x1
+#define HCLGE_ROCEE_RERR_INT_MASK              BIT(0)
+#define HCLGE_ROCEE_BERR_INT_MASK              BIT(1)
+#define HCLGE_ROCEE_ECC_INT_MASK               BIT(2)
+#define HCLGE_ROCEE_OVF_INT_MASK               BIT(3)
+#define HCLGE_ROCEE_OVF_ERR_INT_MASK           0x10000
+#define HCLGE_ROCEE_OVF_ERR_TYPE_MASK          0x3F
 
 enum hclge_err_int_type {
        HCLGE_ERR_INT_MSIX = 0,
@@ -67,9 +105,7 @@ enum hclge_err_int_type {
 struct hclge_hw_blk {
        u32 msk;
        const char *name;
-       int (*enable_error)(struct hclge_dev *hdev, bool en);
-       void (*process_error)(struct hclge_dev *hdev,
-                             enum hclge_err_int_type type);
+       int (*config_err_int)(struct hclge_dev *hdev, bool en);
 };
 
 struct hclge_hw_error {
@@ -78,6 +114,7 @@ struct hclge_hw_error {
 };
 
 int hclge_hw_error_set_state(struct hclge_dev *hdev, bool state);
-int hclge_enable_tm_hw_error(struct hclge_dev *hdev, bool en);
-pci_ers_result_t hclge_process_ras_hw_error(struct hnae3_ae_dev *ae_dev);
+pci_ers_result_t hclge_handle_hw_ras_error(struct hnae3_ae_dev *ae_dev);
+int hclge_handle_hw_msix_error(struct hclge_dev *hdev,
+                              unsigned long *reset_requests);
 #endif
index ffdd960..2238020 100644 (file)
@@ -26,7 +26,7 @@
 #define HCLGE_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset))))
 #define HCLGE_MAC_STATS_FIELD_OFF(f) (offsetof(struct hclge_mac_stats, f))
 
-static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu);
+static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps);
 static int hclge_init_vlan_config(struct hclge_dev *hdev);
 static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
 static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
@@ -48,6 +48,62 @@ static const struct pci_device_id ae_algo_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
 
+static const u32 cmdq_reg_addr_list[] = {HCLGE_CMDQ_TX_ADDR_L_REG,
+                                        HCLGE_CMDQ_TX_ADDR_H_REG,
+                                        HCLGE_CMDQ_TX_DEPTH_REG,
+                                        HCLGE_CMDQ_TX_TAIL_REG,
+                                        HCLGE_CMDQ_TX_HEAD_REG,
+                                        HCLGE_CMDQ_RX_ADDR_L_REG,
+                                        HCLGE_CMDQ_RX_ADDR_H_REG,
+                                        HCLGE_CMDQ_RX_DEPTH_REG,
+                                        HCLGE_CMDQ_RX_TAIL_REG,
+                                        HCLGE_CMDQ_RX_HEAD_REG,
+                                        HCLGE_VECTOR0_CMDQ_SRC_REG,
+                                        HCLGE_CMDQ_INTR_STS_REG,
+                                        HCLGE_CMDQ_INTR_EN_REG,
+                                        HCLGE_CMDQ_INTR_GEN_REG};
+
+static const u32 common_reg_addr_list[] = {HCLGE_MISC_VECTOR_REG_BASE,
+                                          HCLGE_VECTOR0_OTER_EN_REG,
+                                          HCLGE_MISC_RESET_STS_REG,
+                                          HCLGE_MISC_VECTOR_INT_STS,
+                                          HCLGE_GLOBAL_RESET_REG,
+                                          HCLGE_FUN_RST_ING,
+                                          HCLGE_GRO_EN_REG};
+
+static const u32 ring_reg_addr_list[] = {HCLGE_RING_RX_ADDR_L_REG,
+                                        HCLGE_RING_RX_ADDR_H_REG,
+                                        HCLGE_RING_RX_BD_NUM_REG,
+                                        HCLGE_RING_RX_BD_LENGTH_REG,
+                                        HCLGE_RING_RX_MERGE_EN_REG,
+                                        HCLGE_RING_RX_TAIL_REG,
+                                        HCLGE_RING_RX_HEAD_REG,
+                                        HCLGE_RING_RX_FBD_NUM_REG,
+                                        HCLGE_RING_RX_OFFSET_REG,
+                                        HCLGE_RING_RX_FBD_OFFSET_REG,
+                                        HCLGE_RING_RX_STASH_REG,
+                                        HCLGE_RING_RX_BD_ERR_REG,
+                                        HCLGE_RING_TX_ADDR_L_REG,
+                                        HCLGE_RING_TX_ADDR_H_REG,
+                                        HCLGE_RING_TX_BD_NUM_REG,
+                                        HCLGE_RING_TX_PRIORITY_REG,
+                                        HCLGE_RING_TX_TC_REG,
+                                        HCLGE_RING_TX_MERGE_EN_REG,
+                                        HCLGE_RING_TX_TAIL_REG,
+                                        HCLGE_RING_TX_HEAD_REG,
+                                        HCLGE_RING_TX_FBD_NUM_REG,
+                                        HCLGE_RING_TX_OFFSET_REG,
+                                        HCLGE_RING_TX_EBD_NUM_REG,
+                                        HCLGE_RING_TX_EBD_OFFSET_REG,
+                                        HCLGE_RING_TX_BD_ERR_REG,
+                                        HCLGE_RING_EN_REG};
+
+static const u32 tqp_intr_reg_addr_list[] = {HCLGE_TQP_INTR_CTRL_REG,
+                                            HCLGE_TQP_INTR_GL0_REG,
+                                            HCLGE_TQP_INTR_GL1_REG,
+                                            HCLGE_TQP_INTR_GL2_REG,
+                                            HCLGE_TQP_INTR_RL_REG};
+
 static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
        "App    Loopback test",
        "Serdes serial Loopback test",
@@ -921,6 +977,28 @@ static int hclge_config_tso(struct hclge_dev *hdev, int tso_mss_min,
        return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
+static int hclge_config_gro(struct hclge_dev *hdev, bool en)
+{
+       struct hclge_cfg_gro_status_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       if (!hnae3_dev_gro_supported(hdev))
+               return 0;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false);
+       req = (struct hclge_cfg_gro_status_cmd *)desc.data;
+
+       req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "GRO hardware config cmd failed, ret = %d\n", ret);
+
+       return ret;
+}
+
 static int hclge_alloc_tqps(struct hclge_dev *hdev)
 {
        struct hclge_tqp *tqp;
@@ -1144,6 +1222,7 @@ static int hclge_alloc_vport(struct hclge_dev *hdev)
        for (i = 0; i < num_vport; i++) {
                vport->back = hdev;
                vport->vport_id = i;
+               vport->mps = HCLGE_MAC_DEFAULT_FRAME;
 
                if (i == 0)
                        ret = hclge_vport_setup(vport, tqp_main_vport);
@@ -1873,37 +1952,6 @@ static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed,
        return hclge_cfg_mac_speed_dup(hdev, speed, duplex);
 }
 
-static int hclge_query_mac_an_speed_dup(struct hclge_dev *hdev, int *speed,
-                                       u8 *duplex)
-{
-       struct hclge_query_an_speed_dup_cmd *req;
-       struct hclge_desc desc;
-       int speed_tmp;
-       int ret;
-
-       req = (struct hclge_query_an_speed_dup_cmd *)desc.data;
-
-       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true);
-       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac speed/autoneg/duplex query cmd failed %d\n",
-                       ret);
-               return ret;
-       }
-
-       *duplex = hnae3_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_DUPLEX_B);
-       speed_tmp = hnae3_get_field(req->an_syn_dup_speed, HCLGE_QUERY_SPEED_M,
-                                   HCLGE_QUERY_SPEED_S);
-
-       ret = hclge_parse_speed(speed_tmp, speed);
-       if (ret)
-               dev_err(&hdev->pdev->dev,
-                       "could not parse speed(=%d), %d\n", speed_tmp, ret);
-
-       return ret;
-}
-
 static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable)
 {
        struct hclge_config_auto_neg_cmd *req;
@@ -1947,12 +1995,10 @@ static int hclge_get_autoneg(struct hnae3_handle *handle)
 
 static int hclge_mac_init(struct hclge_dev *hdev)
 {
-       struct hnae3_handle *handle = &hdev->vport[0].nic;
-       struct net_device *netdev = handle->kinfo.netdev;
        struct hclge_mac *mac = &hdev->hw.mac;
-       int mtu;
        int ret;
 
+       hdev->support_sfp_query = true;
        hdev->hw.mac.duplex = HCLGE_MAC_FULL;
        ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.speed,
                                         hdev->hw.mac.duplex);
@@ -1964,15 +2010,16 @@ static int hclge_mac_init(struct hclge_dev *hdev)
 
        mac->link = 0;
 
-       if (netdev)
-               mtu = netdev->mtu;
-       else
-               mtu = ETH_DATA_LEN;
+       ret = hclge_set_mac_mtu(hdev, hdev->mps);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "set mtu failed ret=%d\n", ret);
+               return ret;
+       }
 
-       ret = hclge_set_mtu(handle, mtu);
+       ret = hclge_buffer_alloc(hdev);
        if (ret)
                dev_err(&hdev->pdev->dev,
-                       "set mtu failed ret=%d\n", ret);
+                       "allocate buffer fail, ret=%d\n", ret);
 
        return ret;
 }
@@ -2061,34 +2108,58 @@ static void hclge_update_link_status(struct hclge_dev *hdev)
        }
 }
 
+static int hclge_get_sfp_speed(struct hclge_dev *hdev, u32 *speed)
+{
+       struct hclge_sfp_speed_cmd *resp = NULL;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SFP_GET_SPEED, true);
+       resp = (struct hclge_sfp_speed_cmd *)desc.data;
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret == -EOPNOTSUPP) {
+               dev_warn(&hdev->pdev->dev,
+                        "IMP do not support get SFP speed %d\n", ret);
+               return ret;
+       } else if (ret) {
+               dev_err(&hdev->pdev->dev, "get sfp speed failed %d\n", ret);
+               return ret;
+       }
+
+       *speed = resp->sfp_speed;
+
+       return 0;
+}
+
 static int hclge_update_speed_duplex(struct hclge_dev *hdev)
 {
        struct hclge_mac mac = hdev->hw.mac;
-       u8 duplex;
        int speed;
        int ret;
 
-       /* get the speed and duplex as autoneg'result from mac cmd when phy
+       /* get the speed from SFP cmd when phy
         * doesn't exit.
         */
-       if (mac.phydev || !mac.autoneg)
+       if (mac.phydev)
                return 0;
 
-       ret = hclge_query_mac_an_speed_dup(hdev, &speed, &duplex);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac autoneg/speed/duplex query failed %d\n", ret);
-               return ret;
-       }
+       /* if IMP does not support get SFP/qSFP speed, return directly */
+       if (!hdev->support_sfp_query)
+               return 0;
 
-       ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "mac speed/duplex config failed %d\n", ret);
+       ret = hclge_get_sfp_speed(hdev, &speed);
+       if (ret == -EOPNOTSUPP) {
+               hdev->support_sfp_query = false;
+               return ret;
+       } else if (ret) {
                return ret;
        }
 
-       return 0;
+       if (speed == HCLGE_MAC_SPEED_UNKNOWN)
+               return 0; /* do nothing if no SFP */
+
+       /* must config full duplex for SFP */
+       return hclge_cfg_mac_speed_dup(hdev, speed, HCLGE_MAC_FULL);
 }
 
 static int hclge_update_speed_duplex_h(struct hnae3_handle *handle)
@@ -2129,12 +2200,13 @@ static void hclge_service_complete(struct hclge_dev *hdev)
 
 static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
 {
-       u32 rst_src_reg;
-       u32 cmdq_src_reg;
+       u32 rst_src_reg, cmdq_src_reg, msix_src_reg;
 
        /* fetch the events from their corresponding regs */
        rst_src_reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS);
        cmdq_src_reg = hclge_read_dev(&hdev->hw, HCLGE_VECTOR0_CMDQ_SRC_REG);
+       msix_src_reg = hclge_read_dev(&hdev->hw,
+                                     HCLGE_VECTOR0_PF_OTHER_INT_STS_REG);
 
        /* Assumption: If by any chance reset and mailbox events are reported
         * together then we will only process reset event in this go and will
@@ -2144,7 +2216,16 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
         */
 
        /* check for vector0 reset event sources */
+       if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & rst_src_reg) {
+               dev_info(&hdev->pdev->dev, "IMP reset interrupt\n");
+               set_bit(HNAE3_IMP_RESET, &hdev->reset_pending);
+               set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
+               *clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B);
+               return HCLGE_VECTOR0_EVENT_RST;
+       }
+
        if (BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) & rst_src_reg) {
+               dev_info(&hdev->pdev->dev, "global reset interrupt\n");
                set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
                set_bit(HNAE3_GLOBAL_RESET, &hdev->reset_pending);
                *clearval = BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B);
@@ -2152,17 +2233,16 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
        }
 
        if (BIT(HCLGE_VECTOR0_CORERESET_INT_B) & rst_src_reg) {
+               dev_info(&hdev->pdev->dev, "core reset interrupt\n");
                set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
                set_bit(HNAE3_CORE_RESET, &hdev->reset_pending);
                *clearval = BIT(HCLGE_VECTOR0_CORERESET_INT_B);
                return HCLGE_VECTOR0_EVENT_RST;
        }
 
-       if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & rst_src_reg) {
-               set_bit(HNAE3_IMP_RESET, &hdev->reset_pending);
-               *clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B);
-               return HCLGE_VECTOR0_EVENT_RST;
-       }
+       /* check for vector0 msix event source */
+       if (msix_src_reg & HCLGE_VECTOR0_REG_MSIX_MASK)
+               return HCLGE_VECTOR0_EVENT_ERR;
 
        /* check for vector0 mailbox(=CMDQ RX) event source */
        if (BIT(HCLGE_VECTOR0_RX_CMDQ_INT_B) & cmdq_src_reg) {
@@ -2214,6 +2294,19 @@ static irqreturn_t hclge_misc_irq_handle(int irq, void *data)
 
        /* vector 0 interrupt is shared with reset and mailbox source events.*/
        switch (event_cause) {
+       case HCLGE_VECTOR0_EVENT_ERR:
+               /* we do not know what type of reset is required now. This could
+                * only be decided after we fetch the type of errors which
+                * caused this event. Therefore, we will do below for now:
+                * 1. Assert HNAE3_UNKNOWN_RESET type of reset. This means we
+                *    have defered type of reset to be used.
+                * 2. Schedule the reset serivce task.
+                * 3. When service task receives  HNAE3_UNKNOWN_RESET type it
+                *    will fetch the correct type of reset.  This would be done
+                *    by first decoding the types of errors.
+                */
+               set_bit(HNAE3_UNKNOWN_RESET, &hdev->reset_request);
+               /* fall through */
        case HCLGE_VECTOR0_EVENT_RST:
                hclge_reset_task_schedule(hdev);
                break;
@@ -2308,21 +2401,56 @@ static int hclge_notify_client(struct hclge_dev *hdev,
                int ret;
 
                ret = client->ops->reset_notify(handle, type);
-               if (ret)
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "notify nic client failed %d(%d)\n", type, ret);
                        return ret;
+               }
        }
 
        return 0;
 }
 
+static int hclge_notify_roce_client(struct hclge_dev *hdev,
+                                   enum hnae3_reset_notify_type type)
+{
+       struct hnae3_client *client = hdev->roce_client;
+       int ret = 0;
+       u16 i;
+
+       if (!client)
+               return 0;
+
+       if (!client->ops->reset_notify)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < hdev->num_vmdq_vport + 1; i++) {
+               struct hnae3_handle *handle = &hdev->vport[i].roce;
+
+               ret = client->ops->reset_notify(handle, type);
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "notify roce client failed %d(%d)",
+                               type, ret);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
 static int hclge_reset_wait(struct hclge_dev *hdev)
 {
 #define HCLGE_RESET_WATI_MS    100
-#define HCLGE_RESET_WAIT_CNT   5
+#define HCLGE_RESET_WAIT_CNT   200
        u32 val, reg, reg_bit;
        u32 cnt = 0;
 
        switch (hdev->reset_type) {
+       case HNAE3_IMP_RESET:
+               reg = HCLGE_GLOBAL_RESET_REG;
+               reg_bit = HCLGE_IMP_RESET_BIT;
+               break;
        case HNAE3_GLOBAL_RESET:
                reg = HCLGE_GLOBAL_RESET_REG;
                reg_bit = HCLGE_GLOBAL_RESET_BIT;
@@ -2335,6 +2463,8 @@ static int hclge_reset_wait(struct hclge_dev *hdev)
                reg = HCLGE_FUN_RST_ING;
                reg_bit = HCLGE_FUN_RST_ING_B;
                break;
+       case HNAE3_FLR_RESET:
+               break;
        default:
                dev_err(&hdev->pdev->dev,
                        "Wait for unsupported reset type: %d\n",
@@ -2342,6 +2472,20 @@ static int hclge_reset_wait(struct hclge_dev *hdev)
                return -EINVAL;
        }
 
+       if (hdev->reset_type == HNAE3_FLR_RESET) {
+               while (!test_bit(HNAE3_FLR_DONE, &hdev->flr_state) &&
+                      cnt++ < HCLGE_RESET_WAIT_CNT)
+                       msleep(HCLGE_RESET_WATI_MS);
+
+               if (!test_bit(HNAE3_FLR_DONE, &hdev->flr_state)) {
+                       dev_err(&hdev->pdev->dev,
+                               "flr wait timeout: %d\n", cnt);
+                       return -EBUSY;
+               }
+
+               return 0;
+       }
+
        val = hclge_read_dev(&hdev->hw, reg);
        while (hnae3_get_bit(val, reg_bit) && cnt < HCLGE_RESET_WAIT_CNT) {
                msleep(HCLGE_RESET_WATI_MS);
@@ -2358,6 +2502,55 @@ static int hclge_reset_wait(struct hclge_dev *hdev)
        return 0;
 }
 
+static int hclge_set_vf_rst(struct hclge_dev *hdev, int func_id, bool reset)
+{
+       struct hclge_vf_rst_cmd *req;
+       struct hclge_desc desc;
+
+       req = (struct hclge_vf_rst_cmd *)desc.data;
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GBL_RST_STATUS, false);
+       req->dest_vfid = func_id;
+
+       if (reset)
+               req->vf_rst = 0x1;
+
+       return hclge_cmd_send(&hdev->hw, &desc, 1);
+}
+
+int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
+{
+       int i;
+
+       for (i = hdev->num_vmdq_vport + 1; i < hdev->num_alloc_vport; i++) {
+               struct hclge_vport *vport = &hdev->vport[i];
+               int ret;
+
+               /* Send cmd to set/clear VF's FUNC_RST_ING */
+               ret = hclge_set_vf_rst(hdev, vport->vport_id, reset);
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "set vf(%d) rst failed %d!\n",
+                               vport->vport_id, ret);
+                       return ret;
+               }
+
+               if (!reset)
+                       continue;
+
+               /* Inform VF to process the reset.
+                * hclge_inform_reset_assert_to_vf may fail if VF
+                * driver is not loaded.
+                */
+               ret = hclge_inform_reset_assert_to_vf(vport);
+               if (ret)
+                       dev_warn(&hdev->pdev->dev,
+                                "inform reset to vf(%d) failed %d!\n",
+                                vport->vport_id, ret);
+       }
+
+       return 0;
+}
+
 int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id)
 {
        struct hclge_desc desc;
@@ -2396,11 +2589,16 @@ static void hclge_do_reset(struct hclge_dev *hdev)
                break;
        case HNAE3_FUNC_RESET:
                dev_info(&pdev->dev, "PF Reset requested\n");
-               hclge_func_reset_cmd(hdev, 0);
                /* schedule again to check later */
                set_bit(HNAE3_FUNC_RESET, &hdev->reset_pending);
                hclge_reset_task_schedule(hdev);
                break;
+       case HNAE3_FLR_RESET:
+               dev_info(&pdev->dev, "FLR requested\n");
+               /* schedule again to check later */
+               set_bit(HNAE3_FLR_RESET, &hdev->reset_pending);
+               hclge_reset_task_schedule(hdev);
+               break;
        default:
                dev_warn(&pdev->dev,
                         "Unsupported reset type: %d\n", hdev->reset_type);
@@ -2413,21 +2611,46 @@ static enum hnae3_reset_type hclge_get_reset_level(struct hclge_dev *hdev,
 {
        enum hnae3_reset_type rst_level = HNAE3_NONE_RESET;
 
+       /* first, resolve any unknown reset type to the known type(s) */
+       if (test_bit(HNAE3_UNKNOWN_RESET, addr)) {
+               /* we will intentionally ignore any errors from this function
+                *  as we will end up in *some* reset request in any case
+                */
+               hclge_handle_hw_msix_error(hdev, addr);
+               clear_bit(HNAE3_UNKNOWN_RESET, addr);
+               /* We defered the clearing of the error event which caused
+                * interrupt since it was not posssible to do that in
+                * interrupt context (and this is the reason we introduced
+                * new UNKNOWN reset type). Now, the errors have been
+                * handled and cleared in hardware we can safely enable
+                * interrupts. This is an exception to the norm.
+                */
+               hclge_enable_vector(&hdev->misc_vector, true);
+       }
+
        /* return the highest priority reset level amongst all */
-       if (test_bit(HNAE3_GLOBAL_RESET, addr))
+       if (test_bit(HNAE3_IMP_RESET, addr)) {
+               rst_level = HNAE3_IMP_RESET;
+               clear_bit(HNAE3_IMP_RESET, addr);
+               clear_bit(HNAE3_GLOBAL_RESET, addr);
+               clear_bit(HNAE3_CORE_RESET, addr);
+               clear_bit(HNAE3_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_GLOBAL_RESET, addr)) {
                rst_level = HNAE3_GLOBAL_RESET;
-       else if (test_bit(HNAE3_CORE_RESET, addr))
+               clear_bit(HNAE3_GLOBAL_RESET, addr);
+               clear_bit(HNAE3_CORE_RESET, addr);
+               clear_bit(HNAE3_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_CORE_RESET, addr)) {
                rst_level = HNAE3_CORE_RESET;
-       else if (test_bit(HNAE3_IMP_RESET, addr))
-               rst_level = HNAE3_IMP_RESET;
-       else if (test_bit(HNAE3_FUNC_RESET, addr))
+               clear_bit(HNAE3_CORE_RESET, addr);
+               clear_bit(HNAE3_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_FUNC_RESET, addr)) {
                rst_level = HNAE3_FUNC_RESET;
-
-       /* now, clear all other resets */
-       clear_bit(HNAE3_GLOBAL_RESET, addr);
-       clear_bit(HNAE3_CORE_RESET, addr);
-       clear_bit(HNAE3_IMP_RESET, addr);
-       clear_bit(HNAE3_FUNC_RESET, addr);
+               clear_bit(HNAE3_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_FLR_RESET, addr)) {
+               rst_level = HNAE3_FLR_RESET;
+               clear_bit(HNAE3_FLR_RESET, addr);
+       }
 
        return rst_level;
 }
@@ -2457,39 +2680,206 @@ static void hclge_clear_reset_cause(struct hclge_dev *hdev)
        hclge_enable_vector(&hdev->misc_vector, true);
 }
 
+static int hclge_reset_prepare_down(struct hclge_dev *hdev)
+{
+       int ret = 0;
+
+       switch (hdev->reset_type) {
+       case HNAE3_FUNC_RESET:
+               /* fall through */
+       case HNAE3_FLR_RESET:
+               ret = hclge_set_all_vf_rst(hdev, true);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int hclge_reset_prepare_wait(struct hclge_dev *hdev)
+{
+       u32 reg_val;
+       int ret = 0;
+
+       switch (hdev->reset_type) {
+       case HNAE3_FUNC_RESET:
+               /* There is no mechanism for PF to know if VF has stopped IO
+                * for now, just wait 100 ms for VF to stop IO
+                */
+               msleep(100);
+               ret = hclge_func_reset_cmd(hdev, 0);
+               if (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "asserting function reset fail %d!\n", ret);
+                       return ret;
+               }
+
+               /* After performaning pf reset, it is not necessary to do the
+                * mailbox handling or send any command to firmware, because
+                * any mailbox handling or command to firmware is only valid
+                * after hclge_cmd_init is called.
+                */
+               set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
+               break;
+       case HNAE3_FLR_RESET:
+               /* There is no mechanism for PF to know if VF has stopped IO
+                * for now, just wait 100 ms for VF to stop IO
+                */
+               msleep(100);
+               set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state);
+               set_bit(HNAE3_FLR_DOWN, &hdev->flr_state);
+               break;
+       case HNAE3_IMP_RESET:
+               reg_val = hclge_read_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG);
+               hclge_write_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG,
+                               BIT(HCLGE_VECTOR0_IMP_RESET_INT_B) | reg_val);
+               break;
+       default:
+               break;
+       }
+
+       dev_info(&hdev->pdev->dev, "prepare wait ok\n");
+
+       return ret;
+}
+
+static bool hclge_reset_err_handle(struct hclge_dev *hdev, bool is_timeout)
+{
+#define MAX_RESET_FAIL_CNT 5
+#define RESET_UPGRADE_DELAY_SEC 10
+
+       if (hdev->reset_pending) {
+               dev_info(&hdev->pdev->dev, "Reset pending %lu\n",
+                        hdev->reset_pending);
+               return true;
+       } else if ((hdev->reset_type != HNAE3_IMP_RESET) &&
+                  (hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG) &
+                   BIT(HCLGE_IMP_RESET_BIT))) {
+               dev_info(&hdev->pdev->dev,
+                        "reset failed because IMP Reset is pending\n");
+               hclge_clear_reset_cause(hdev);
+               return false;
+       } else if (hdev->reset_fail_cnt < MAX_RESET_FAIL_CNT) {
+               hdev->reset_fail_cnt++;
+               if (is_timeout) {
+                       set_bit(hdev->reset_type, &hdev->reset_pending);
+                       dev_info(&hdev->pdev->dev,
+                                "re-schedule to wait for hw reset done\n");
+                       return true;
+               }
+
+               dev_info(&hdev->pdev->dev, "Upgrade reset level\n");
+               hclge_clear_reset_cause(hdev);
+               mod_timer(&hdev->reset_timer,
+                         jiffies + RESET_UPGRADE_DELAY_SEC * HZ);
+
+               return false;
+       }
+
+       hclge_clear_reset_cause(hdev);
+       dev_err(&hdev->pdev->dev, "Reset fail!\n");
+       return false;
+}
+
+static int hclge_reset_prepare_up(struct hclge_dev *hdev)
+{
+       int ret = 0;
+
+       switch (hdev->reset_type) {
+       case HNAE3_FUNC_RESET:
+               /* fall through */
+       case HNAE3_FLR_RESET:
+               ret = hclge_set_all_vf_rst(hdev, false);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
 static void hclge_reset(struct hclge_dev *hdev)
 {
        struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
-       struct hnae3_handle *handle;
+       bool is_timeout = false;
+       int ret;
 
        /* Initialize ae_dev reset status as well, in case enet layer wants to
         * know if device is undergoing reset
         */
        ae_dev->reset_type = hdev->reset_type;
+       hdev->reset_count++;
+       hdev->last_reset_time = jiffies;
        /* perform reset of the stack & ae device for a client */
-       handle = &hdev->vport[0].nic;
+       ret = hclge_notify_roce_client(hdev, HNAE3_DOWN_CLIENT);
+       if (ret)
+               goto err_reset;
+
+       ret = hclge_reset_prepare_down(hdev);
+       if (ret)
+               goto err_reset;
+
        rtnl_lock();
-       hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
+       ret = hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
+       if (ret)
+               goto err_reset_lock;
+
        rtnl_unlock();
 
-       if (!hclge_reset_wait(hdev)) {
-               rtnl_lock();
-               hclge_notify_client(hdev, HNAE3_UNINIT_CLIENT);
-               hclge_reset_ae_dev(hdev->ae_dev);
-               hclge_notify_client(hdev, HNAE3_INIT_CLIENT);
+       ret = hclge_reset_prepare_wait(hdev);
+       if (ret)
+               goto err_reset;
 
-               hclge_clear_reset_cause(hdev);
-       } else {
-               rtnl_lock();
-               /* schedule again to check pending resets later */
-               set_bit(hdev->reset_type, &hdev->reset_pending);
-               hclge_reset_task_schedule(hdev);
+       if (hclge_reset_wait(hdev)) {
+               is_timeout = true;
+               goto err_reset;
        }
 
-       hclge_notify_client(hdev, HNAE3_UP_CLIENT);
-       handle->last_reset_time = jiffies;
+       ret = hclge_notify_roce_client(hdev, HNAE3_UNINIT_CLIENT);
+       if (ret)
+               goto err_reset;
+
+       rtnl_lock();
+       ret = hclge_notify_client(hdev, HNAE3_UNINIT_CLIENT);
+       if (ret)
+               goto err_reset_lock;
+
+       ret = hclge_reset_ae_dev(hdev->ae_dev);
+       if (ret)
+               goto err_reset_lock;
+
+       ret = hclge_notify_client(hdev, HNAE3_INIT_CLIENT);
+       if (ret)
+               goto err_reset_lock;
+
+       hclge_clear_reset_cause(hdev);
+
+       ret = hclge_reset_prepare_up(hdev);
+       if (ret)
+               goto err_reset_lock;
+
+       ret = hclge_notify_client(hdev, HNAE3_UP_CLIENT);
+       if (ret)
+               goto err_reset_lock;
+
        rtnl_unlock();
-       ae_dev->reset_type = HNAE3_NONE_RESET;
+
+       ret = hclge_notify_roce_client(hdev, HNAE3_INIT_CLIENT);
+       if (ret)
+               goto err_reset;
+
+       ret = hclge_notify_roce_client(hdev, HNAE3_UP_CLIENT);
+       if (ret)
+               goto err_reset;
+
+       return;
+
+err_reset_lock:
+       rtnl_unlock();
+err_reset:
+       if (hclge_reset_err_handle(hdev, is_timeout))
+               hclge_reset_task_schedule(hdev);
 }
 
 static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
@@ -2515,20 +2905,42 @@ static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
        if (!handle)
                handle = &hdev->vport[0].nic;
 
-       if (time_before(jiffies, (handle->last_reset_time + 3 * HZ)))
+       if (time_before(jiffies, (hdev->last_reset_time + 3 * HZ)))
                return;
-       else if (time_after(jiffies, (handle->last_reset_time + 4 * 5 * HZ)))
-               handle->reset_level = HNAE3_FUNC_RESET;
+       else if (hdev->default_reset_request)
+               hdev->reset_level =
+                       hclge_get_reset_level(hdev,
+                                             &hdev->default_reset_request);
+       else if (time_after(jiffies, (hdev->last_reset_time + 4 * 5 * HZ)))
+               hdev->reset_level = HNAE3_FUNC_RESET;
 
        dev_info(&hdev->pdev->dev, "received reset event , reset type is %d",
-                handle->reset_level);
+                hdev->reset_level);
 
        /* request reset & schedule reset task */
-       set_bit(handle->reset_level, &hdev->reset_request);
+       set_bit(hdev->reset_level, &hdev->reset_request);
        hclge_reset_task_schedule(hdev);
 
-       if (handle->reset_level < HNAE3_GLOBAL_RESET)
-               handle->reset_level++;
+       if (hdev->reset_level < HNAE3_GLOBAL_RESET)
+               hdev->reset_level++;
+}
+
+static void hclge_set_def_reset_request(struct hnae3_ae_dev *ae_dev,
+                                       enum hnae3_reset_type rst_type)
+{
+       struct hclge_dev *hdev = ae_dev->priv;
+
+       set_bit(rst_type, &hdev->default_reset_request);
+}
+
+static void hclge_reset_timer(struct timer_list *t)
+{
+       struct hclge_dev *hdev = from_timer(hdev, t, reset_timer);
+
+       dev_info(&hdev->pdev->dev,
+                "triggering global reset in reset timer\n");
+       set_bit(HNAE3_GLOBAL_RESET, &hdev->default_reset_request);
+       hclge_reset_event(hdev->pdev, NULL);
 }
 
 static void hclge_reset_subtask(struct hclge_dev *hdev)
@@ -2542,6 +2954,7 @@ static void hclge_reset_subtask(struct hclge_dev *hdev)
         *    b. else, we can come back later to check this status so re-sched
         *       now.
         */
+       hdev->last_reset_time = jiffies;
        hdev->reset_type = hclge_get_reset_level(hdev, &hdev->reset_pending);
        if (hdev->reset_type != HNAE3_NONE_RESET)
                hclge_reset(hdev);
@@ -2584,6 +2997,23 @@ static void hclge_mailbox_service_task(struct work_struct *work)
        clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state);
 }
 
+static void hclge_update_vport_alive(struct hclge_dev *hdev)
+{
+       int i;
+
+       /* start from vport 1 for PF is always alive */
+       for (i = 1; i < hdev->num_alloc_vport; i++) {
+               struct hclge_vport *vport = &hdev->vport[i];
+
+               if (time_after(jiffies, vport->last_active_jiffies + 8 * HZ))
+                       clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+
+               /* If vf is not alive, set to default value */
+               if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
+                       vport->mps = HCLGE_MAC_DEFAULT_FRAME;
+       }
+}
+
 static void hclge_service_task(struct work_struct *work)
 {
        struct hclge_dev *hdev =
@@ -2596,6 +3026,7 @@ static void hclge_service_task(struct work_struct *work)
 
        hclge_update_speed_duplex(hdev);
        hclge_update_link_status(hdev);
+       hclge_update_vport_alive(hdev);
        hclge_service_complete(hdev);
 }
 
@@ -4336,8 +4767,12 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
        struct hlist_node *node;
        int ret;
 
+       /* Return ok here, because reset error handling will check this
+        * return value. If error is returned here, the reset process will
+        * fail.
+        */
        if (!hnae3_dev_fd_supported(hdev))
-               return -EOPNOTSUPP;
+               return 0;
 
        hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
                ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
@@ -4592,6 +5027,31 @@ static int hclge_get_all_rules(struct hnae3_handle *handle,
        return 0;
 }
 
+static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       return hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG) ||
+              hclge_read_dev(&hdev->hw, HCLGE_FUN_RST_ING);
+}
+
+static bool hclge_ae_dev_resetting(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
+}
+
+static unsigned long hclge_ae_dev_reset_cnt(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       return hdev->reset_count;
+}
+
 static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
@@ -4805,10 +5265,6 @@ static int hclge_ae_start(struct hnae3_handle *handle)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
-       int i;
-
-       for (i = 0; i < vport->alloc_tqps; i++)
-               hclge_tqp_enable(hdev, i, 0, true);
 
        /* mac enable */
        hclge_cfg_mac_mode(hdev, true);
@@ -4828,7 +5284,6 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
-       int i;
 
        set_bit(HCLGE_STATE_DOWN, &hdev->state);
 
@@ -4836,14 +5291,15 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
        cancel_work_sync(&hdev->service_task);
        clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state);
 
-       if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) {
+       /* If it is not PF reset, the firmware will disable the MAC,
+        * so it only need to stop phy here.
+        */
+       if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) &&
+           hdev->reset_type != HNAE3_FUNC_RESET) {
                hclge_mac_stop_phy(hdev);
                return;
        }
 
-       for (i = 0; i < vport->alloc_tqps; i++)
-               hclge_tqp_enable(hdev, i, 0, false);
-
        /* Mac disable */
        hclge_cfg_mac_mode(hdev, false);
 
@@ -4856,6 +5312,32 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
        hclge_update_link_status(hdev);
 }
 
+int hclge_vport_start(struct hclge_vport *vport)
+{
+       set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+       vport->last_active_jiffies = jiffies;
+       return 0;
+}
+
+void hclge_vport_stop(struct hclge_vport *vport)
+{
+       clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+}
+
+static int hclge_client_start(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+
+       return hclge_vport_start(vport);
+}
+
+static void hclge_client_stop(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+
+       hclge_vport_stop(vport);
+}
+
 static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
                                         u16 cmdq_resp, u8  resp_code,
                                         enum hclge_mac_vlan_tbl_opcode op)
@@ -6003,54 +6485,76 @@ int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable)
        return hclge_set_vlan_rx_offload_cfg(vport);
 }
 
-static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mtu)
+static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps)
 {
        struct hclge_config_max_frm_size_cmd *req;
        struct hclge_desc desc;
-       int max_frm_size;
-       int ret;
-
-       max_frm_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
-
-       if (max_frm_size < HCLGE_MAC_MIN_FRAME ||
-           max_frm_size > HCLGE_MAC_MAX_FRAME)
-               return -EINVAL;
-
-       max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME);
 
        hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, false);
 
        req = (struct hclge_config_max_frm_size_cmd *)desc.data;
-       req->max_frm_size = cpu_to_le16(max_frm_size);
+       req->max_frm_size = cpu_to_le16(new_mps);
        req->min_frm_size = HCLGE_MAC_MIN_FRAME;
 
-       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
-       if (ret)
-               dev_err(&hdev->pdev->dev, "set mtu fail, ret =%d.\n", ret);
-       else
-               hdev->mps = max_frm_size;
-
-       return ret;
+       return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
 static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
+
+       return hclge_set_vport_mtu(vport, new_mtu);
+}
+
+int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu)
+{
        struct hclge_dev *hdev = vport->back;
-       int ret;
+       int i, max_frm_size, ret = 0;
 
-       ret = hclge_set_mac_mtu(hdev, new_mtu);
+       max_frm_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN;
+       if (max_frm_size < HCLGE_MAC_MIN_FRAME ||
+           max_frm_size > HCLGE_MAC_MAX_FRAME)
+               return -EINVAL;
+
+       max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME);
+       mutex_lock(&hdev->vport_lock);
+       /* VF's mps must fit within hdev->mps */
+       if (vport->vport_id && max_frm_size > hdev->mps) {
+               mutex_unlock(&hdev->vport_lock);
+               return -EINVAL;
+       } else if (vport->vport_id) {
+               vport->mps = max_frm_size;
+               mutex_unlock(&hdev->vport_lock);
+               return 0;
+       }
+
+       /* PF's mps must be greater then VF's mps */
+       for (i = 1; i < hdev->num_alloc_vport; i++)
+               if (max_frm_size < hdev->vport[i].mps) {
+                       mutex_unlock(&hdev->vport_lock);
+                       return -EINVAL;
+               }
+
+       hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
+
+       ret = hclge_set_mac_mtu(hdev, max_frm_size);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "Change mtu fail, ret =%d\n", ret);
-               return ret;
+               goto out;
        }
 
+       hdev->mps = max_frm_size;
+       vport->mps = max_frm_size;
+
        ret = hclge_buffer_alloc(hdev);
        if (ret)
                dev_err(&hdev->pdev->dev,
                        "Allocate buffer fail, ret =%d\n", ret);
 
+out:
+       hclge_notify_client(hdev, HNAE3_UP_CLIENT);
+       mutex_unlock(&hdev->vport_lock);
        return ret;
 }
 
@@ -6250,7 +6754,7 @@ int hclge_cfg_flowctrl(struct hclge_dev *hdev)
        if (!phydev->link || !phydev->autoneg)
                return 0;
 
-       local_advertising = ethtool_adv_to_lcl_adv_t(phydev->advertising);
+       local_advertising = linkmode_adv_to_lcl_adv_t(phydev->advertising);
 
        if (phydev->pause)
                remote_advertising = LPA_PAUSE_CAP;
@@ -6612,6 +7116,8 @@ static void hclge_state_uninit(struct hclge_dev *hdev)
 
        if (hdev->service_timer.function)
                del_timer_sync(&hdev->service_timer);
+       if (hdev->reset_timer.function)
+               del_timer_sync(&hdev->reset_timer);
        if (hdev->service_task.func)
                cancel_work_sync(&hdev->service_task);
        if (hdev->rst_service_task.func)
@@ -6620,6 +7126,34 @@ static void hclge_state_uninit(struct hclge_dev *hdev)
                cancel_work_sync(&hdev->mbx_service_task);
 }
 
+static void hclge_flr_prepare(struct hnae3_ae_dev *ae_dev)
+{
+#define HCLGE_FLR_WAIT_MS      100
+#define HCLGE_FLR_WAIT_CNT     50
+       struct hclge_dev *hdev = ae_dev->priv;
+       int cnt = 0;
+
+       clear_bit(HNAE3_FLR_DOWN, &hdev->flr_state);
+       clear_bit(HNAE3_FLR_DONE, &hdev->flr_state);
+       set_bit(HNAE3_FLR_RESET, &hdev->default_reset_request);
+       hclge_reset_event(hdev->pdev, NULL);
+
+       while (!test_bit(HNAE3_FLR_DOWN, &hdev->flr_state) &&
+              cnt++ < HCLGE_FLR_WAIT_CNT)
+               msleep(HCLGE_FLR_WAIT_MS);
+
+       if (!test_bit(HNAE3_FLR_DOWN, &hdev->flr_state))
+               dev_err(&hdev->pdev->dev,
+                       "flr wait down timeout: %d\n", cnt);
+}
+
+static void hclge_flr_done(struct hnae3_ae_dev *ae_dev)
+{
+       struct hclge_dev *hdev = ae_dev->priv;
+
+       set_bit(HNAE3_FLR_DONE, &hdev->flr_state);
+}
+
 static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct pci_dev *pdev = ae_dev->pdev;
@@ -6635,7 +7169,11 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        hdev->pdev = pdev;
        hdev->ae_dev = ae_dev;
        hdev->reset_type = HNAE3_NONE_RESET;
+       hdev->reset_level = HNAE3_FUNC_RESET;
        ae_dev->priv = hdev;
+       hdev->mps = ETH_FRAME_LEN + ETH_FCS_LEN + 2 * VLAN_HLEN;
+
+       mutex_init(&hdev->vport_lock);
 
        ret = hclge_pci_init(hdev);
        if (ret) {
@@ -6727,6 +7265,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
                goto err_mdiobus_unreg;
        }
 
+       ret = hclge_config_gro(hdev, true);
+       if (ret)
+               goto err_mdiobus_unreg;
+
        ret = hclge_init_vlan_config(hdev);
        if (ret) {
                dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
@@ -6762,13 +7304,14 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        ret = hclge_hw_error_set_state(hdev, true);
        if (ret) {
                dev_err(&pdev->dev,
-                       "hw error interrupts enable failed, ret =%d\n", ret);
+                       "fail(%d) to enable hw error interrupts\n", ret);
                goto err_mdiobus_unreg;
        }
 
        hclge_dcb_ops_set(hdev);
 
        timer_setup(&hdev->service_timer, hclge_service_timer, 0);
+       timer_setup(&hdev->reset_timer, hclge_reset_timer, 0);
        INIT_WORK(&hdev->service_task, hclge_service_task);
        INIT_WORK(&hdev->rst_service_task, hclge_reset_service_task);
        INIT_WORK(&hdev->mbx_service_task, hclge_mailbox_service_task);
@@ -6779,6 +7322,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        hclge_enable_vector(&hdev->misc_vector, true);
 
        hclge_state_init(hdev);
+       hdev->last_reset_time = jiffies;
 
        pr_info("%s driver initialization finished.\n", HCLGE_DRIVER_NAME);
        return 0;
@@ -6806,6 +7350,17 @@ static void hclge_stats_clear(struct hclge_dev *hdev)
        memset(&hdev->hw_stats, 0, sizeof(hdev->hw_stats));
 }
 
+static void hclge_reset_vport_state(struct hclge_dev *hdev)
+{
+       struct hclge_vport *vport = hdev->vport;
+       int i;
+
+       for (i = 0; i < hdev->num_alloc_vport; i++) {
+               hclge_vport_start(vport);
+               vport++;
+       }
+}
+
 static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct hclge_dev *hdev = ae_dev->priv;
@@ -6856,6 +7411,10 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
+       ret = hclge_config_gro(hdev, true);
+       if (ret)
+               return ret;
+
        ret = hclge_init_vlan_config(hdev);
        if (ret) {
                dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
@@ -6881,11 +7440,17 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
-       /* Re-enable the TM hw error interrupts because
-        * they get disabled on core/global reset.
+       /* Re-enable the hw error interrupts because
+        * the interrupts get disabled on core/global reset.
         */
-       if (hclge_enable_tm_hw_error(hdev, true))
-               dev_err(&pdev->dev, "failed to enable TM hw error interrupts\n");
+       ret = hclge_hw_error_set_state(hdev, true);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "fail(%d) to re-enable HNS hw error interrupts\n", ret);
+               return ret;
+       }
+
+       hclge_reset_vport_state(hdev);
 
        dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
                 HCLGE_DRIVER_NAME);
@@ -6913,6 +7478,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
        hclge_destroy_cmd_queue(&hdev->hw);
        hclge_misc_irq_uninit(hdev);
        hclge_pci_uninit(hdev);
+       mutex_destroy(&hdev->vport_lock);
        ae_dev->priv = NULL;
 }
 
@@ -7166,8 +7732,15 @@ static int hclge_get_64_bit_regs(struct hclge_dev *hdev, u32 regs_num,
        return 0;
 }
 
+#define MAX_SEPARATE_NUM       4
+#define SEPARATOR_VALUE                0xFFFFFFFF
+#define REG_NUM_PER_LINE       4
+#define REG_LEN_PER_LINE       (REG_NUM_PER_LINE * sizeof(u32))
+
 static int hclge_get_regs_len(struct hnae3_handle *handle)
 {
+       int cmdq_lines, common_lines, ring_lines, tqp_intr_lines;
+       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
        u32 regs_num_32_bit, regs_num_64_bit;
@@ -7180,15 +7753,25 @@ static int hclge_get_regs_len(struct hnae3_handle *handle)
                return -EOPNOTSUPP;
        }
 
-       return regs_num_32_bit * sizeof(u32) + regs_num_64_bit * sizeof(u64);
+       cmdq_lines = sizeof(cmdq_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       common_lines = sizeof(common_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       ring_lines = sizeof(ring_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       tqp_intr_lines = sizeof(tqp_intr_reg_addr_list) / REG_LEN_PER_LINE + 1;
+
+       return (cmdq_lines + common_lines + ring_lines * kinfo->num_tqps +
+               tqp_intr_lines * (hdev->num_msi_used - 1)) * REG_LEN_PER_LINE +
+               regs_num_32_bit * sizeof(u32) + regs_num_64_bit * sizeof(u64);
 }
 
 static void hclge_get_regs(struct hnae3_handle *handle, u32 *version,
                           void *data)
 {
+       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
        u32 regs_num_32_bit, regs_num_64_bit;
+       int i, j, reg_um, separator_num;
+       u32 *reg = data;
        int ret;
 
        *version = hdev->fw_version;
@@ -7200,16 +7783,53 @@ static void hclge_get_regs(struct hnae3_handle *handle, u32 *version,
                return;
        }
 
-       ret = hclge_get_32_bit_regs(hdev, regs_num_32_bit, data);
+       /* fetching per-PF registers valus from PF PCIe register space */
+       reg_um = sizeof(cmdq_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclge_read_dev(&hdev->hw, cmdq_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(common_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclge_read_dev(&hdev->hw, common_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(ring_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < kinfo->num_tqps; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclge_read_dev(&hdev->hw,
+                                               ring_reg_addr_list[i] +
+                                               0x200 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       reg_um = sizeof(tqp_intr_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_msi_used - 1; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclge_read_dev(&hdev->hw,
+                                               tqp_intr_reg_addr_list[i] +
+                                               4 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       /* fetching PF common registers values from firmware */
+       ret = hclge_get_32_bit_regs(hdev, regs_num_32_bit, reg);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "Get 32 bit register failed, ret = %d.\n", ret);
                return;
        }
 
-       data = (u32 *)data + regs_num_32_bit;
-       ret = hclge_get_64_bit_regs(hdev, regs_num_64_bit,
-                                   data);
+       reg += regs_num_32_bit;
+       ret = hclge_get_64_bit_regs(hdev, regs_num_64_bit, reg);
        if (ret)
                dev_err(&hdev->pdev->dev,
                        "Get 64 bit register failed, ret = %d.\n", ret);
@@ -7272,9 +7892,19 @@ static void hclge_get_link_mode(struct hnae3_handle *handle,
        }
 }
 
+static int hclge_gro_en(struct hnae3_handle *handle, int enable)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       return hclge_config_gro(hdev, enable);
+}
+
 static const struct hnae3_ae_ops hclge_ops = {
        .init_ae_dev = hclge_init_ae_dev,
        .uninit_ae_dev = hclge_uninit_ae_dev,
+       .flr_prepare = hclge_flr_prepare,
+       .flr_done = hclge_flr_done,
        .init_client_instance = hclge_init_client_instance,
        .uninit_client_instance = hclge_uninit_client_instance,
        .map_ring_to_vector = hclge_map_ring_to_vector,
@@ -7285,6 +7915,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_loopback = hclge_set_loopback,
        .start = hclge_ae_start,
        .stop = hclge_ae_stop,
+       .client_start = hclge_client_start,
+       .client_stop = hclge_client_stop,
        .get_status = hclge_get_status,
        .get_ksettings_an_result = hclge_get_ksettings_an_result,
        .update_speed_duplex_h = hclge_update_speed_duplex_h,
@@ -7321,6 +7953,7 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_vf_vlan_filter = hclge_set_vf_vlan_filter,
        .enable_hw_strip_rxvtag = hclge_en_hw_strip_rxvtag,
        .reset_event = hclge_reset_event,
+       .set_default_reset_request = hclge_set_def_reset_request,
        .get_tqps_and_rss_info = hclge_get_tqps_and_rss_info,
        .set_channels = hclge_set_channels,
        .get_channels = hclge_get_channels,
@@ -7336,7 +7969,12 @@ static const struct hnae3_ae_ops hclge_ops = {
        .get_fd_all_rules = hclge_get_all_rules,
        .restore_fd_rules = hclge_restore_fd_entries,
        .enable_fd = hclge_enable_fd,
-       .process_hw_error = hclge_process_ras_hw_error,
+       .dbg_run_cmd = hclge_dbg_run_cmd,
+       .handle_hw_ras_error = hclge_handle_hw_ras_error,
+       .get_hw_reset_stat = hclge_get_hw_reset_stat,
+       .ae_dev_resetting = hclge_ae_dev_resetting,
+       .ae_dev_reset_cnt = hclge_ae_dev_reset_cnt,
+       .set_gro_en = hclge_gro_en,
 };
 
 static struct hnae3_ae_algo ae_algo = {
index 0d92154..106fce1 100644 (file)
 #define HCLGE_VECTOR_REG_OFFSET                0x4
 #define HCLGE_VECTOR_VF_OFFSET         0x100000
 
+#define HCLGE_CMDQ_TX_ADDR_L_REG       0x27000
+#define HCLGE_CMDQ_TX_ADDR_H_REG       0x27004
+#define HCLGE_CMDQ_TX_DEPTH_REG                0x27008
+#define HCLGE_CMDQ_TX_TAIL_REG         0x27010
+#define HCLGE_CMDQ_TX_HEAD_REG         0x27014
+#define HCLGE_CMDQ_RX_ADDR_L_REG       0x27018
+#define HCLGE_CMDQ_RX_ADDR_H_REG       0x2701C
+#define HCLGE_CMDQ_RX_DEPTH_REG                0x27020
+#define HCLGE_CMDQ_RX_TAIL_REG         0x27024
+#define HCLGE_CMDQ_RX_HEAD_REG         0x27028
+#define HCLGE_CMDQ_INTR_SRC_REG                0x27100
+#define HCLGE_CMDQ_INTR_STS_REG                0x27104
+#define HCLGE_CMDQ_INTR_EN_REG         0x27108
+#define HCLGE_CMDQ_INTR_GEN_REG                0x2710C
+
+/* bar registers for common func */
+#define HCLGE_VECTOR0_OTER_EN_REG      0x20600
+#define HCLGE_RAS_OTHER_STS_REG                0x20B00
+#define HCLGE_FUNC_RESET_STS_REG       0x20C00
+#define HCLGE_GRO_EN_REG               0x28000
+
+/* bar registers for rcb */
+#define HCLGE_RING_RX_ADDR_L_REG       0x80000
+#define HCLGE_RING_RX_ADDR_H_REG       0x80004
+#define HCLGE_RING_RX_BD_NUM_REG       0x80008
+#define HCLGE_RING_RX_BD_LENGTH_REG    0x8000C
+#define HCLGE_RING_RX_MERGE_EN_REG     0x80014
+#define HCLGE_RING_RX_TAIL_REG         0x80018
+#define HCLGE_RING_RX_HEAD_REG         0x8001C
+#define HCLGE_RING_RX_FBD_NUM_REG      0x80020
+#define HCLGE_RING_RX_OFFSET_REG       0x80024
+#define HCLGE_RING_RX_FBD_OFFSET_REG   0x80028
+#define HCLGE_RING_RX_STASH_REG                0x80030
+#define HCLGE_RING_RX_BD_ERR_REG       0x80034
+#define HCLGE_RING_TX_ADDR_L_REG       0x80040
+#define HCLGE_RING_TX_ADDR_H_REG       0x80044
+#define HCLGE_RING_TX_BD_NUM_REG       0x80048
+#define HCLGE_RING_TX_PRIORITY_REG     0x8004C
+#define HCLGE_RING_TX_TC_REG           0x80050
+#define HCLGE_RING_TX_MERGE_EN_REG     0x80054
+#define HCLGE_RING_TX_TAIL_REG         0x80058
+#define HCLGE_RING_TX_HEAD_REG         0x8005C
+#define HCLGE_RING_TX_FBD_NUM_REG      0x80060
+#define HCLGE_RING_TX_OFFSET_REG       0x80064
+#define HCLGE_RING_TX_EBD_NUM_REG      0x80068
+#define HCLGE_RING_TX_EBD_OFFSET_REG   0x80070
+#define HCLGE_RING_TX_BD_ERR_REG       0x80074
+#define HCLGE_RING_EN_REG              0x80090
+
+/* bar registers for tqp interrupt */
+#define HCLGE_TQP_INTR_CTRL_REG                0x20000
+#define HCLGE_TQP_INTR_GL0_REG         0x20100
+#define HCLGE_TQP_INTR_GL1_REG         0x20200
+#define HCLGE_TQP_INTR_GL2_REG         0x20300
+#define HCLGE_TQP_INTR_RL_REG          0x20900
+
 #define HCLGE_RSS_IND_TBL_SIZE         512
 #define HCLGE_RSS_SET_BITMAP_MSK       GENMASK(15, 0)
 #define HCLGE_RSS_KEY_SIZE             40
@@ -97,11 +153,13 @@ enum HLCGE_PORT_TYPE {
 #define HCLGE_NETWORK_PORT_ID_M                GENMASK(3, 0)
 
 /* Reset related Registers */
+#define HCLGE_PF_OTHER_INT_REG         0x20600
 #define HCLGE_MISC_RESET_STS_REG       0x20700
 #define HCLGE_MISC_VECTOR_INT_STS      0x20800
 #define HCLGE_GLOBAL_RESET_REG         0x20A00
 #define HCLGE_GLOBAL_RESET_BIT         0
 #define HCLGE_CORE_RESET_BIT           1
+#define HCLGE_IMP_RESET_BIT            2
 #define HCLGE_FUN_RST_ING              0x20C00
 #define HCLGE_FUN_RST_ING_B            0
 
@@ -115,8 +173,10 @@ enum HLCGE_PORT_TYPE {
 /* CMDQ register bits for RX event(=MBX event) */
 #define HCLGE_VECTOR0_RX_CMDQ_INT_B    1
 
+#define HCLGE_VECTOR0_IMP_RESET_INT_B  1
+
 #define HCLGE_MAC_DEFAULT_FRAME \
-       (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN)
+       (ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN + ETH_DATA_LEN)
 #define HCLGE_MAC_MIN_FRAME            64
 #define HCLGE_MAC_MAX_FRAME            9728
 
@@ -145,12 +205,14 @@ enum HCLGE_DEV_STATE {
 enum hclge_evt_cause {
        HCLGE_VECTOR0_EVENT_RST,
        HCLGE_VECTOR0_EVENT_MBX,
+       HCLGE_VECTOR0_EVENT_ERR,
        HCLGE_VECTOR0_EVENT_OTHER,
 };
 
 #define HCLGE_MPF_ENBALE 1
 
 enum HCLGE_MAC_SPEED {
+       HCLGE_MAC_SPEED_UNKNOWN = 0,            /* unknown */
        HCLGE_MAC_SPEED_10M     = 10,           /* 10 Mbps */
        HCLGE_MAC_SPEED_100M    = 100,          /* 100 Mbps */
        HCLGE_MAC_SPEED_1G      = 1000,         /* 1000 Mbps   = 1 Gbps */
@@ -593,10 +655,16 @@ struct hclge_dev {
        struct hclge_misc_vector misc_vector;
        struct hclge_hw_stats hw_stats;
        unsigned long state;
+       unsigned long flr_state;
+       unsigned long last_reset_time;
 
        enum hnae3_reset_type reset_type;
+       enum hnae3_reset_type reset_level;
+       unsigned long default_reset_request;
        unsigned long reset_request;    /* reset has been requested */
        unsigned long reset_pending;    /* client rst is pending to be served */
+       unsigned long reset_count;      /* the number of reset has been done */
+       u32 reset_fail_cnt;
        u32 fw_version;
        u16 num_vmdq_vport;             /* Num vmdq vport this PF has set up */
        u16 num_tqps;                   /* Num task queue pairs of this PF */
@@ -614,6 +682,7 @@ struct hclge_dev {
        u8 hw_tc_map;
        u8 tc_num_last_time;
        enum hclge_fc_mode fc_mode_last_time;
+       u8 support_sfp_query;
 
 #define HCLGE_FLAG_TC_BASE_SCH_MODE            1
 #define HCLGE_FLAG_VNET_BASE_SCH_MODE          2
@@ -644,6 +713,7 @@ struct hclge_dev {
        unsigned long service_timer_period;
        unsigned long service_timer_previous;
        struct timer_list service_timer;
+       struct timer_list reset_timer;
        struct work_struct service_task;
        struct work_struct rst_service_task;
        struct work_struct mbx_service_task;
@@ -667,6 +737,8 @@ struct hclge_dev {
 
        u32 pkt_buf_size; /* Total pf buf size for tx/rx */
        u32 mps; /* Max packet size */
+       /* vport_lock protect resource shared by vports */
+       struct mutex vport_lock;
 
        struct hclge_vlan_type_cfg vlan_type_cfg;
 
@@ -717,6 +789,11 @@ struct hclge_rss_tuple_cfg {
        u8 ipv6_fragment_en;
 };
 
+enum HCLGE_VPORT_STATE {
+       HCLGE_VPORT_STATE_ALIVE,
+       HCLGE_VPORT_STATE_MAX
+};
+
 struct hclge_vport {
        u16 alloc_tqps; /* Allocated Tx/Rx queues */
 
@@ -742,6 +819,10 @@ struct hclge_vport {
        struct hclge_dev *back;  /* Back reference to associated dev */
        struct hnae3_handle nic;
        struct hnae3_handle roce;
+
+       unsigned long state;
+       unsigned long last_active_jiffies;
+       u32 mps; /* Max packet size */
 };
 
 void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc,
@@ -768,6 +849,12 @@ static inline int hclge_get_queue_id(struct hnae3_queue *queue)
        return tqp->index;
 }
 
+static inline bool hclge_is_reset_pending(struct hclge_dev *hdev)
+{
+       return !!hdev->reset_pending;
+}
+
+int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport);
 int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex);
 int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto,
                          u16 vlan_id, bool is_kill);
@@ -777,9 +864,14 @@ int hclge_buffer_alloc(struct hclge_dev *hdev);
 int hclge_rss_init_hw(struct hclge_dev *hdev);
 void hclge_rss_indir_init_cfg(struct hclge_dev *hdev);
 
+int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport);
 void hclge_mbx_handler(struct hclge_dev *hdev);
 int hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id);
 void hclge_reset_vf_queue(struct hclge_vport *vport, u16 queue_id);
 int hclge_cfg_flowctrl(struct hclge_dev *hdev);
 int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id);
+int hclge_vport_start(struct hclge_vport *vport);
+void hclge_vport_stop(struct hclge_vport *vport);
+int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu);
+int hclge_dbg_run_cmd(struct hnae3_handle *handle, char *cmd_buf);
 #endif
index f890022..e16a730 100644 (file)
@@ -79,15 +79,26 @@ static int hclge_send_mbx_msg(struct hclge_vport *vport, u8 *msg, u16 msg_len,
        return status;
 }
 
-static int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport)
+int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport)
 {
+       struct hclge_dev *hdev = vport->back;
+       enum hnae3_reset_type reset_type;
        u8 msg_data[2];
        u8 dest_vfid;
 
        dest_vfid = (u8)vport->vport_id;
 
+       if (hdev->reset_type == HNAE3_FUNC_RESET)
+               reset_type = HNAE3_VF_PF_FUNC_RESET;
+       else if (hdev->reset_type == HNAE3_FLR_RESET)
+               reset_type = HNAE3_VF_FULL_RESET;
+       else
+               return -EINVAL;
+
+       memcpy(&msg_data[0], &reset_type, sizeof(u16));
+
        /* send this requested info to VF */
-       return hclge_send_mbx_msg(vport, msg_data, sizeof(u8),
+       return hclge_send_mbx_msg(vport, msg_data, sizeof(msg_data),
                                  HCLGE_MBX_ASSERTING_RESET, dest_vfid);
 }
 
@@ -290,6 +301,21 @@ static int hclge_set_vf_vlan_cfg(struct hclge_vport *vport,
        return status;
 }
 
+static int hclge_set_vf_alive(struct hclge_vport *vport,
+                             struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                             bool gen_resp)
+{
+       bool alive = !!mbx_req->msg[2];
+       int ret = 0;
+
+       if (alive)
+               ret = hclge_vport_start(vport);
+       else
+               hclge_vport_stop(vport);
+
+       return ret;
+}
+
 static int hclge_get_vf_tcinfo(struct hclge_vport *vport,
                               struct hclge_mbx_vf_to_pf_cmd *mbx_req,
                               bool gen_resp)
@@ -363,24 +389,28 @@ static void hclge_reset_vf(struct hclge_vport *vport,
        int ret;
 
        dev_warn(&hdev->pdev->dev, "PF received VF reset request from VF %d!",
-                mbx_req->mbx_src_vfid);
-
-       /* Acknowledge VF that PF is now about to assert the reset for the VF.
-        * On receiving this message VF will get into pending state and will
-        * start polling for the hardware reset completion status.
-        */
-       ret = hclge_inform_reset_assert_to_vf(vport);
-       if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "PF fail(%d) to inform VF(%d)of reset, reset failed!\n",
-                       ret, vport->vport_id);
-               return;
-       }
+                vport->vport_id);
 
-       dev_warn(&hdev->pdev->dev, "PF is now resetting VF %d.\n",
-                mbx_req->mbx_src_vfid);
-       /* reset this virtual function */
-       hclge_func_reset_cmd(hdev, mbx_req->mbx_src_vfid);
+       ret = hclge_func_reset_cmd(hdev, vport->vport_id);
+       hclge_gen_resp_to_vf(vport, mbx_req, ret, NULL, 0);
+}
+
+static void hclge_vf_keep_alive(struct hclge_vport *vport,
+                               struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       vport->last_active_jiffies = jiffies;
+}
+
+static int hclge_set_vf_mtu(struct hclge_vport *vport,
+                           struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       int ret;
+       u32 mtu;
+
+       memcpy(&mtu, &mbx_req->msg[2], sizeof(mtu));
+       ret = hclge_set_vport_mtu(vport, mtu);
+
+       return hclge_gen_resp_to_vf(vport, mbx_req, ret, NULL, 0);
 }
 
 static bool hclge_cmd_crq_empty(struct hclge_hw *hw)
@@ -460,6 +490,13 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                                        "PF failed(%d) to config VF's VLAN\n",
                                        ret);
                        break;
+               case HCLGE_MBX_SET_ALIVE:
+                       ret = hclge_set_vf_alive(vport, req, false);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to set VF's ALIVE\n",
+                                       ret);
+                       break;
                case HCLGE_MBX_GET_QINFO:
                        ret = hclge_get_vf_queue_info(vport, req, true);
                        if (ret)
@@ -487,6 +524,15 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                case HCLGE_MBX_RESET:
                        hclge_reset_vf(vport, req);
                        break;
+               case HCLGE_MBX_KEEP_ALIVE:
+                       hclge_vf_keep_alive(vport, req);
+                       break;
+               case HCLGE_MBX_SET_MTU:
+                       ret = hclge_set_vf_mtu(vport, req);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "VF fail(%d) to set mtu\n", ret);
+                       break;
                default:
                        dev_err(&hdev->pdev->dev,
                                "un-supported mailbox message, code = %d\n",
index 0301863..741cb3b 100644 (file)
@@ -195,12 +195,13 @@ int hclge_mac_connect_phy(struct hclge_dev *hdev)
 {
        struct net_device *netdev = hdev->vport[0].nic.netdev;
        struct phy_device *phydev = hdev->hw.mac.phydev;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        int ret;
 
        if (!phydev)
                return 0;
 
-       phydev->supported &= ~SUPPORTED_FIBRE;
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
 
        ret = phy_connect_direct(netdev, phydev,
                                 hclge_mac_adjust_link,
@@ -210,7 +211,15 @@ int hclge_mac_connect_phy(struct hclge_dev *hdev)
                return ret;
        }
 
-       phydev->supported &= HCLGE_PHY_SUPPORTED_FEATURES;
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, mask);
+       linkmode_set_bit_array(phy_10_100_features_array,
+                              ARRAY_SIZE(phy_10_100_features_array),
+                              mask);
+       linkmode_set_bit_array(phy_gbit_features_array,
+                              ARRAY_SIZE(phy_gbit_features_array),
+                              mask);
+       linkmode_and(phydev->supported, phydev->supported, mask);
        phy_support_asym_pause(phydev);
 
        return 0;
index 494e562..00458da 100644 (file)
@@ -1259,15 +1259,13 @@ int hclge_pause_setup_hw(struct hclge_dev *hdev)
        return 0;
 }
 
-int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
+void hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
 {
        struct hclge_vport *vport = hdev->vport;
        struct hnae3_knic_private_info *kinfo;
        u32 i, k;
 
        for (i = 0; i < HNAE3_MAX_USER_PRIO; i++) {
-               if (prio_tc[i] >= hdev->tm_info.num_tc)
-                       return -EINVAL;
                hdev->tm_info.prio_tc[i] = prio_tc[i];
 
                for (k = 0;  k < hdev->num_alloc_vport; k++) {
@@ -1275,18 +1273,12 @@ int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
                        kinfo->prio_tc[i] = prio_tc[i];
                }
        }
-       return 0;
 }
 
-int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
+void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
 {
        u8 i, bit_map = 0;
 
-       for (i = 0; i < hdev->num_alloc_vport; i++) {
-               if (num_tc > hdev->vport[i].alloc_tqps)
-                       return -EINVAL;
-       }
-
        hdev->tm_info.num_tc = num_tc;
 
        for (i = 0; i < hdev->tm_info.num_tc; i++)
@@ -1300,8 +1292,6 @@ int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
        hdev->hw_tc_map = bit_map;
 
        hclge_tm_schd_info_init(hdev);
-
-       return 0;
 }
 
 int hclge_tm_init_hw(struct hclge_dev *hdev)
index 25eef13..9c6192c 100644 (file)
@@ -55,6 +55,12 @@ struct hclge_qs_weight_cmd {
        u8 dwrr;
 };
 
+struct hclge_ets_tc_weight_cmd {
+       u8 tc_weight[HNAE3_MAX_TC];
+       u8 weight_offset;
+       u8 rsvd[15];
+};
+
 #define HCLGE_TM_SHAP_IR_B_MSK  GENMASK(7, 0)
 #define HCLGE_TM_SHAP_IR_B_LSH 0
 #define HCLGE_TM_SHAP_IR_U_MSK  GENMASK(11, 8)
@@ -131,8 +137,8 @@ struct hclge_port_shapping_cmd {
 int hclge_tm_schd_init(struct hclge_dev *hdev);
 int hclge_pause_setup_hw(struct hclge_dev *hdev);
 int hclge_tm_schd_mode_hw(struct hclge_dev *hdev);
-int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc);
-int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
+void hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc);
+void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
 int hclge_tm_dwrr_cfg(struct hclge_dev *hdev);
 int hclge_tm_map_cfg(struct hclge_dev *hdev);
 int hclge_tm_init_hw(struct hclge_dev *hdev);
index 0d3b445..d5765c8 100644 (file)
@@ -72,6 +72,45 @@ static bool hclgevf_is_special_opcode(u16 opcode)
        return false;
 }
 
+static void hclgevf_cmd_config_regs(struct hclgevf_cmq_ring *ring)
+{
+       struct hclgevf_dev *hdev = ring->dev;
+       struct hclgevf_hw *hw = &hdev->hw;
+       u32 reg_val;
+
+       if (ring->flag == HCLGEVF_TYPE_CSQ) {
+               reg_val = (u32)ring->desc_dma_addr;
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_BASEADDR_L_REG, reg_val);
+               reg_val = (u32)((ring->desc_dma_addr >> 31) >> 1);
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_BASEADDR_H_REG, reg_val);
+
+               reg_val = (ring->desc_num >> HCLGEVF_NIC_CMQ_DESC_NUM_S);
+               reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_DEPTH_REG, reg_val);
+
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_HEAD_REG, 0);
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_TAIL_REG, 0);
+       } else {
+               reg_val = (u32)ring->desc_dma_addr;
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_BASEADDR_L_REG, reg_val);
+               reg_val = (u32)((ring->desc_dma_addr >> 31) >> 1);
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_BASEADDR_H_REG, reg_val);
+
+               reg_val = (ring->desc_num >> HCLGEVF_NIC_CMQ_DESC_NUM_S);
+               reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_DEPTH_REG, reg_val);
+
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_HEAD_REG, 0);
+               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_TAIL_REG, 0);
+       }
+}
+
+static void hclgevf_cmd_init_regs(struct hclgevf_hw *hw)
+{
+       hclgevf_cmd_config_regs(&hw->cmq.csq);
+       hclgevf_cmd_config_regs(&hw->cmq.crq);
+}
+
 static int hclgevf_alloc_cmd_desc(struct hclgevf_cmq_ring *ring)
 {
        int size = ring->desc_num * sizeof(struct hclgevf_desc);
@@ -96,61 +135,23 @@ static void hclgevf_free_cmd_desc(struct hclgevf_cmq_ring *ring)
        }
 }
 
-static int hclgevf_init_cmd_queue(struct hclgevf_dev *hdev,
-                                 struct hclgevf_cmq_ring *ring)
+static int hclgevf_alloc_cmd_queue(struct hclgevf_dev *hdev, int ring_type)
 {
        struct hclgevf_hw *hw = &hdev->hw;
-       int ring_type = ring->flag;
-       u32 reg_val;
+       struct hclgevf_cmq_ring *ring =
+               (ring_type == HCLGEVF_TYPE_CSQ) ? &hw->cmq.csq : &hw->cmq.crq;
        int ret;
 
-       ring->desc_num = HCLGEVF_NIC_CMQ_DESC_NUM;
-       spin_lock_init(&ring->lock);
-       ring->next_to_clean = 0;
-       ring->next_to_use = 0;
        ring->dev = hdev;
+       ring->flag = ring_type;
 
        /* allocate CSQ/CRQ descriptor */
        ret = hclgevf_alloc_cmd_desc(ring);
-       if (ret) {
+       if (ret)
                dev_err(&hdev->pdev->dev, "failed(%d) to alloc %s desc\n", ret,
                        (ring_type == HCLGEVF_TYPE_CSQ) ? "CSQ" : "CRQ");
-               return ret;
-       }
 
-       /* initialize the hardware registers with csq/crq dma-address,
-        * descriptor number, head & tail pointers
-        */
-       switch (ring_type) {
-       case HCLGEVF_TYPE_CSQ:
-               reg_val = (u32)ring->desc_dma_addr;
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_BASEADDR_L_REG, reg_val);
-               reg_val = (u32)((ring->desc_dma_addr >> 31) >> 1);
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_BASEADDR_H_REG, reg_val);
-
-               reg_val = (ring->desc_num >> HCLGEVF_NIC_CMQ_DESC_NUM_S);
-               reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_DEPTH_REG, reg_val);
-
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_HEAD_REG, 0);
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CSQ_TAIL_REG, 0);
-               return 0;
-       case HCLGEVF_TYPE_CRQ:
-               reg_val = (u32)ring->desc_dma_addr;
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_BASEADDR_L_REG, reg_val);
-               reg_val = (u32)((ring->desc_dma_addr >> 31) >> 1);
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_BASEADDR_H_REG, reg_val);
-
-               reg_val = (ring->desc_num >> HCLGEVF_NIC_CMQ_DESC_NUM_S);
-               reg_val |= HCLGEVF_NIC_CMQ_ENABLE;
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_DEPTH_REG, reg_val);
-
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_HEAD_REG, 0);
-               hclgevf_write_dev(hw, HCLGEVF_NIC_CRQ_TAIL_REG, 0);
-               return 0;
-       default:
-               return -EINVAL;
-       }
+       return ret;
 }
 
 void hclgevf_cmd_setup_basic_desc(struct hclgevf_desc *desc,
@@ -188,7 +189,8 @@ int hclgevf_cmd_send(struct hclgevf_hw *hw, struct hclgevf_desc *desc, int num)
 
        spin_lock_bh(&hw->cmq.csq.lock);
 
-       if (num > hclgevf_ring_space(&hw->cmq.csq)) {
+       if (num > hclgevf_ring_space(&hw->cmq.csq) ||
+           test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state)) {
                spin_unlock_bh(&hw->cmq.csq.lock);
                return -EBUSY;
        }
@@ -282,55 +284,83 @@ static int  hclgevf_cmd_query_firmware_version(struct hclgevf_hw *hw,
        return status;
 }
 
-int hclgevf_cmd_init(struct hclgevf_dev *hdev)
+int hclgevf_cmd_queue_init(struct hclgevf_dev *hdev)
 {
-       u32 version;
        int ret;
 
-       /* setup Tx write back timeout */
+       /* Setup the lock for command queue */
+       spin_lock_init(&hdev->hw.cmq.csq.lock);
+       spin_lock_init(&hdev->hw.cmq.crq.lock);
+
        hdev->hw.cmq.tx_timeout = HCLGEVF_CMDQ_TX_TIMEOUT;
+       hdev->hw.cmq.csq.desc_num = HCLGEVF_NIC_CMQ_DESC_NUM;
+       hdev->hw.cmq.crq.desc_num = HCLGEVF_NIC_CMQ_DESC_NUM;
 
-       /* setup queue CSQ/CRQ rings */
-       hdev->hw.cmq.csq.flag = HCLGEVF_TYPE_CSQ;
-       ret = hclgevf_init_cmd_queue(hdev, &hdev->hw.cmq.csq);
+       ret = hclgevf_alloc_cmd_queue(hdev, HCLGEVF_TYPE_CSQ);
        if (ret) {
                dev_err(&hdev->pdev->dev,
-                       "failed(%d) to initialize CSQ ring\n", ret);
+                       "CSQ ring setup error %d\n", ret);
                return ret;
        }
 
-       hdev->hw.cmq.crq.flag = HCLGEVF_TYPE_CRQ;
-       ret = hclgevf_init_cmd_queue(hdev, &hdev->hw.cmq.crq);
+       ret = hclgevf_alloc_cmd_queue(hdev, HCLGEVF_TYPE_CRQ);
        if (ret) {
                dev_err(&hdev->pdev->dev,
-                       "failed(%d) to initialize CRQ ring\n", ret);
+                       "CRQ ring setup error %d\n", ret);
                goto err_csq;
        }
 
+       return 0;
+err_csq:
+       hclgevf_free_cmd_desc(&hdev->hw.cmq.csq);
+       return ret;
+}
+
+int hclgevf_cmd_init(struct hclgevf_dev *hdev)
+{
+       u32 version;
+       int ret;
+
+       spin_lock_bh(&hdev->hw.cmq.csq.lock);
+       spin_lock_bh(&hdev->hw.cmq.crq.lock);
+
        /* initialize the pointers of async rx queue of mailbox */
        hdev->arq.hdev = hdev;
        hdev->arq.head = 0;
        hdev->arq.tail = 0;
        hdev->arq.count = 0;
+       hdev->hw.cmq.csq.next_to_clean = 0;
+       hdev->hw.cmq.csq.next_to_use = 0;
+       hdev->hw.cmq.crq.next_to_clean = 0;
+       hdev->hw.cmq.crq.next_to_use = 0;
+
+       hclgevf_cmd_init_regs(&hdev->hw);
+
+       spin_unlock_bh(&hdev->hw.cmq.crq.lock);
+       spin_unlock_bh(&hdev->hw.cmq.csq.lock);
+
+       clear_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state);
+
+       /* Check if there is new reset pending, because the higher level
+        * reset may happen when lower level reset is being processed.
+        */
+       if (hclgevf_is_reset_pending(hdev)) {
+               set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state);
+               return -EBUSY;
+       }
 
        /* get firmware version */
        ret = hclgevf_cmd_query_firmware_version(&hdev->hw, &version);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "failed(%d) to query firmware version\n", ret);
-               goto err_crq;
+               return ret;
        }
        hdev->fw_version = version;
 
        dev_info(&hdev->pdev->dev, "The firmware version is %08x\n", version);
 
        return 0;
-err_crq:
-       hclgevf_free_cmd_desc(&hdev->hw.cmq.crq);
-err_csq:
-       hclgevf_free_cmd_desc(&hdev->hw.cmq.csq);
-
-       return ret;
 }
 
 void hclgevf_cmd_uninit(struct hclgevf_dev *hdev)
index bc294b0..47030b4 100644 (file)
@@ -87,6 +87,8 @@ enum hclgevf_opcode_type {
        HCLGEVF_OPC_QUERY_TX_STATUS     = 0x0B03,
        HCLGEVF_OPC_QUERY_RX_STATUS     = 0x0B13,
        HCLGEVF_OPC_CFG_COM_TQP_QUEUE   = 0x0B20,
+       /* GRO command */
+       HCLGEVF_OPC_GRO_GENERIC_CONFIG  = 0x0C10,
        /* RSS cmd */
        HCLGEVF_OPC_RSS_GENERIC_CONFIG  = 0x0D01,
        HCLGEVF_OPC_RSS_INPUT_TUPLE     = 0x0D02,
@@ -149,6 +151,12 @@ struct hclgevf_query_res_cmd {
        __le16 rsv[7];
 };
 
+#define HCLGEVF_GRO_EN_B               0
+struct hclgevf_cfg_gro_status_cmd {
+       __le16 gro_en;
+       u8 rsv[22];
+};
+
 #define HCLGEVF_RSS_DEFAULT_OUTPORT_B  4
 #define HCLGEVF_RSS_HASH_KEY_OFFSET_B  4
 #define HCLGEVF_RSS_HASH_KEY_NUM       16
@@ -256,6 +264,7 @@ static inline u32 hclgevf_read_reg(u8 __iomem *base, u32 reg)
 
 int hclgevf_cmd_init(struct hclgevf_dev *hdev);
 void hclgevf_cmd_uninit(struct hclgevf_dev *hdev);
+int hclgevf_cmd_queue_init(struct hclgevf_dev *hdev);
 
 int hclgevf_cmd_send(struct hclgevf_hw *hw, struct hclgevf_desc *desc, int num);
 void hclgevf_cmd_setup_basic_desc(struct hclgevf_desc *desc,
index 085edb9..417e078 100644 (file)
@@ -2,6 +2,7 @@
 // Copyright (c) 2016-2017 Hisilicon Limited.
 
 #include <linux/etherdevice.h>
+#include <linux/iopoll.h>
 #include <net/rtnetlink.h>
 #include "hclgevf_cmd.h"
 #include "hclgevf_main.h"
@@ -10,8 +11,7 @@
 
 #define HCLGEVF_NAME   "hclgevf"
 
-static int hclgevf_init_hdev(struct hclgevf_dev *hdev);
-static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev);
+static int hclgevf_reset_hdev(struct hclgevf_dev *hdev);
 static struct hnae3_ae_algo ae_algovf;
 
 static const struct pci_device_id ae_algovf_pci_tbl[] = {
@@ -23,6 +23,58 @@ static const struct pci_device_id ae_algovf_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, ae_algovf_pci_tbl);
 
+static const u32 cmdq_reg_addr_list[] = {HCLGEVF_CMDQ_TX_ADDR_L_REG,
+                                        HCLGEVF_CMDQ_TX_ADDR_H_REG,
+                                        HCLGEVF_CMDQ_TX_DEPTH_REG,
+                                        HCLGEVF_CMDQ_TX_TAIL_REG,
+                                        HCLGEVF_CMDQ_TX_HEAD_REG,
+                                        HCLGEVF_CMDQ_RX_ADDR_L_REG,
+                                        HCLGEVF_CMDQ_RX_ADDR_H_REG,
+                                        HCLGEVF_CMDQ_RX_DEPTH_REG,
+                                        HCLGEVF_CMDQ_RX_TAIL_REG,
+                                        HCLGEVF_CMDQ_RX_HEAD_REG,
+                                        HCLGEVF_VECTOR0_CMDQ_SRC_REG,
+                                        HCLGEVF_CMDQ_INTR_STS_REG,
+                                        HCLGEVF_CMDQ_INTR_EN_REG,
+                                        HCLGEVF_CMDQ_INTR_GEN_REG};
+
+static const u32 common_reg_addr_list[] = {HCLGEVF_MISC_VECTOR_REG_BASE,
+                                          HCLGEVF_RST_ING,
+                                          HCLGEVF_GRO_EN_REG};
+
+static const u32 ring_reg_addr_list[] = {HCLGEVF_RING_RX_ADDR_L_REG,
+                                        HCLGEVF_RING_RX_ADDR_H_REG,
+                                        HCLGEVF_RING_RX_BD_NUM_REG,
+                                        HCLGEVF_RING_RX_BD_LENGTH_REG,
+                                        HCLGEVF_RING_RX_MERGE_EN_REG,
+                                        HCLGEVF_RING_RX_TAIL_REG,
+                                        HCLGEVF_RING_RX_HEAD_REG,
+                                        HCLGEVF_RING_RX_FBD_NUM_REG,
+                                        HCLGEVF_RING_RX_OFFSET_REG,
+                                        HCLGEVF_RING_RX_FBD_OFFSET_REG,
+                                        HCLGEVF_RING_RX_STASH_REG,
+                                        HCLGEVF_RING_RX_BD_ERR_REG,
+                                        HCLGEVF_RING_TX_ADDR_L_REG,
+                                        HCLGEVF_RING_TX_ADDR_H_REG,
+                                        HCLGEVF_RING_TX_BD_NUM_REG,
+                                        HCLGEVF_RING_TX_PRIORITY_REG,
+                                        HCLGEVF_RING_TX_TC_REG,
+                                        HCLGEVF_RING_TX_MERGE_EN_REG,
+                                        HCLGEVF_RING_TX_TAIL_REG,
+                                        HCLGEVF_RING_TX_HEAD_REG,
+                                        HCLGEVF_RING_TX_FBD_NUM_REG,
+                                        HCLGEVF_RING_TX_OFFSET_REG,
+                                        HCLGEVF_RING_TX_EBD_NUM_REG,
+                                        HCLGEVF_RING_TX_EBD_OFFSET_REG,
+                                        HCLGEVF_RING_TX_BD_ERR_REG,
+                                        HCLGEVF_RING_EN_REG};
+
+static const u32 tqp_intr_reg_addr_list[] = {HCLGEVF_TQP_INTR_CTRL_REG,
+                                            HCLGEVF_TQP_INTR_GL0_REG,
+                                            HCLGEVF_TQP_INTR_GL1_REG,
+                                            HCLGEVF_TQP_INTR_GL2_REG,
+                                            HCLGEVF_TQP_INTR_RL_REG};
+
 static inline struct hclgevf_dev *hclgevf_ae_get_hdev(
        struct hnae3_handle *handle)
 {
@@ -209,12 +261,6 @@ static int hclgevf_alloc_tqps(struct hclgevf_dev *hdev)
        struct hclgevf_tqp *tqp;
        int i;
 
-       /* if this is on going reset then we need to re-allocate the TPQs
-        * since we cannot assume we would get same number of TPQs back from PF
-        */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               devm_kfree(&hdev->pdev->dev, hdev->htqp);
-
        hdev->htqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps,
                                  sizeof(struct hclgevf_tqp), GFP_KERNEL);
        if (!hdev->htqp)
@@ -258,12 +304,6 @@ static int hclgevf_knic_setup(struct hclgevf_dev *hdev)
        new_tqps = kinfo->rss_size * kinfo->num_tc;
        kinfo->num_tqps = min(new_tqps, hdev->num_tqps);
 
-       /* if this is on going reset then we need to re-allocate the hnae queues
-        * as well since number of TPQs from PF might have changed.
-        */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               devm_kfree(&hdev->pdev->dev, kinfo->tqp);
-
        kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, kinfo->num_tqps,
                                  sizeof(struct hnae3_queue *), GFP_KERNEL);
        if (!kinfo->tqp)
@@ -868,6 +908,9 @@ static int hclgevf_unmap_ring_from_vector(
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
        int ret, vector_id;
 
+       if (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state))
+               return 0;
+
        vector_id = hclgevf_get_vector_index(hdev, vector);
        if (vector_id < 0) {
                dev_err(&handle->pdev->dev,
@@ -956,13 +999,6 @@ static int hclgevf_tqp_enable(struct hclgevf_dev *hdev, int tqp_id,
        return status;
 }
 
-static int hclgevf_get_queue_id(struct hnae3_queue *queue)
-{
-       struct hclgevf_tqp *tqp = container_of(queue, struct hclgevf_tqp, q);
-
-       return tqp->index;
-}
-
 static void hclgevf_reset_tqp_stats(struct hnae3_handle *handle)
 {
        struct hnae3_knic_private_info *kinfo = &handle->kinfo;
@@ -1097,38 +1133,87 @@ static int hclgevf_reset_tqp(struct hnae3_handle *handle, u16 queue_id)
                                    2, true, NULL, 0);
 }
 
+static int hclgevf_set_mtu(struct hnae3_handle *handle, int new_mtu)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_MTU, 0, (u8 *)&new_mtu,
+                                   sizeof(new_mtu), true, NULL, 0);
+}
+
 static int hclgevf_notify_client(struct hclgevf_dev *hdev,
                                 enum hnae3_reset_notify_type type)
 {
        struct hnae3_client *client = hdev->nic_client;
        struct hnae3_handle *handle = &hdev->nic;
+       int ret;
 
        if (!client->ops->reset_notify)
                return -EOPNOTSUPP;
 
-       return client->ops->reset_notify(handle, type);
+       ret = client->ops->reset_notify(handle, type);
+       if (ret)
+               dev_err(&hdev->pdev->dev, "notify nic client failed %d(%d)\n",
+                       type, ret);
+
+       return ret;
+}
+
+static void hclgevf_flr_done(struct hnae3_ae_dev *ae_dev)
+{
+       struct hclgevf_dev *hdev = ae_dev->priv;
+
+       set_bit(HNAE3_FLR_DONE, &hdev->flr_state);
+}
+
+static int hclgevf_flr_poll_timeout(struct hclgevf_dev *hdev,
+                                   unsigned long delay_us,
+                                   unsigned long wait_cnt)
+{
+       unsigned long cnt = 0;
+
+       while (!test_bit(HNAE3_FLR_DONE, &hdev->flr_state) &&
+              cnt++ < wait_cnt)
+               usleep_range(delay_us, delay_us * 2);
+
+       if (!test_bit(HNAE3_FLR_DONE, &hdev->flr_state)) {
+               dev_err(&hdev->pdev->dev,
+                       "flr wait timeout\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
 }
 
 static int hclgevf_reset_wait(struct hclgevf_dev *hdev)
 {
-#define HCLGEVF_RESET_WAIT_MS  500
-#define HCLGEVF_RESET_WAIT_CNT 20
-       u32 val, cnt = 0;
+#define HCLGEVF_RESET_WAIT_US  20000
+#define HCLGEVF_RESET_WAIT_CNT 2000
+#define HCLGEVF_RESET_WAIT_TIMEOUT_US  \
+       (HCLGEVF_RESET_WAIT_US * HCLGEVF_RESET_WAIT_CNT)
+
+       u32 val;
+       int ret;
 
        /* wait to check the hardware reset completion status */
-       val = hclgevf_read_dev(&hdev->hw, HCLGEVF_FUN_RST_ING);
-       while (hnae3_get_bit(val, HCLGEVF_FUN_RST_ING_B) &&
-              (cnt < HCLGEVF_RESET_WAIT_CNT)) {
-               msleep(HCLGEVF_RESET_WAIT_MS);
-               val = hclgevf_read_dev(&hdev->hw, HCLGEVF_FUN_RST_ING);
-               cnt++;
-       }
+       val = hclgevf_read_dev(&hdev->hw, HCLGEVF_RST_ING);
+       dev_info(&hdev->pdev->dev, "checking vf resetting status: %x\n", val);
+
+       if (hdev->reset_type == HNAE3_FLR_RESET)
+               return hclgevf_flr_poll_timeout(hdev,
+                                               HCLGEVF_RESET_WAIT_US,
+                                               HCLGEVF_RESET_WAIT_CNT);
+
+       ret = readl_poll_timeout(hdev->hw.io_base + HCLGEVF_RST_ING, val,
+                                !(val & HCLGEVF_RST_ING_BITS),
+                                HCLGEVF_RESET_WAIT_US,
+                                HCLGEVF_RESET_WAIT_TIMEOUT_US);
 
        /* hardware completion status should be available by this time */
-       if (cnt >= HCLGEVF_RESET_WAIT_CNT) {
-               dev_warn(&hdev->pdev->dev,
-                        "could'nt get reset done status from h/w, timeout!\n");
-               return -EBUSY;
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "could'nt get reset done status from h/w, timeout!\n");
+               return ret;
        }
 
        /* we will wait a bit more to let reset of the stack to complete. This
@@ -1145,10 +1230,12 @@ static int hclgevf_reset_stack(struct hclgevf_dev *hdev)
        int ret;
 
        /* uninitialize the nic client */
-       hclgevf_notify_client(hdev, HNAE3_UNINIT_CLIENT);
+       ret = hclgevf_notify_client(hdev, HNAE3_UNINIT_CLIENT);
+       if (ret)
+               return ret;
 
        /* re-initialize the hclge device */
-       ret = hclgevf_init_hdev(hdev);
+       ret = hclgevf_reset_hdev(hdev);
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "hclge device re-init failed, VF is disabled!\n");
@@ -1156,22 +1243,60 @@ static int hclgevf_reset_stack(struct hclgevf_dev *hdev)
        }
 
        /* bring up the nic client again */
-       hclgevf_notify_client(hdev, HNAE3_INIT_CLIENT);
+       ret = hclgevf_notify_client(hdev, HNAE3_INIT_CLIENT);
+       if (ret)
+               return ret;
 
        return 0;
 }
 
+static int hclgevf_reset_prepare_wait(struct hclgevf_dev *hdev)
+{
+       int ret = 0;
+
+       switch (hdev->reset_type) {
+       case HNAE3_VF_FUNC_RESET:
+               ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_RESET, 0, NULL,
+                                          0, true, NULL, sizeof(u8));
+               break;
+       case HNAE3_FLR_RESET:
+               set_bit(HNAE3_FLR_DOWN, &hdev->flr_state);
+               break;
+       default:
+               break;
+       }
+
+       set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state);
+
+       dev_info(&hdev->pdev->dev, "prepare reset(%d) wait done, ret:%d\n",
+                hdev->reset_type, ret);
+
+       return ret;
+}
+
 static int hclgevf_reset(struct hclgevf_dev *hdev)
 {
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
        int ret;
 
+       /* Initialize ae_dev reset status as well, in case enet layer wants to
+        * know if device is undergoing reset
+        */
+       ae_dev->reset_type = hdev->reset_type;
+       hdev->reset_count++;
        rtnl_lock();
 
        /* bring down the nic to stop any ongoing TX/RX */
-       hclgevf_notify_client(hdev, HNAE3_DOWN_CLIENT);
+       ret = hclgevf_notify_client(hdev, HNAE3_DOWN_CLIENT);
+       if (ret)
+               goto err_reset_lock;
 
        rtnl_unlock();
 
+       ret = hclgevf_reset_prepare_wait(hdev);
+       if (ret)
+               goto err_reset;
+
        /* check if VF could successfully fetch the hardware reset completion
         * status from the hardware
         */
@@ -1181,58 +1306,118 @@ static int hclgevf_reset(struct hclgevf_dev *hdev)
                dev_err(&hdev->pdev->dev,
                        "VF failed(=%d) to fetch H/W reset completion status\n",
                        ret);
-
-               dev_warn(&hdev->pdev->dev, "VF reset failed, disabling VF!\n");
-               rtnl_lock();
-               hclgevf_notify_client(hdev, HNAE3_UNINIT_CLIENT);
-
-               rtnl_unlock();
-               return ret;
+               goto err_reset;
        }
 
        rtnl_lock();
 
        /* now, re-initialize the nic client and ae device*/
        ret = hclgevf_reset_stack(hdev);
-       if (ret)
+       if (ret) {
                dev_err(&hdev->pdev->dev, "failed to reset VF stack\n");
+               goto err_reset_lock;
+       }
 
        /* bring up the nic to enable TX/RX again */
-       hclgevf_notify_client(hdev, HNAE3_UP_CLIENT);
+       ret = hclgevf_notify_client(hdev, HNAE3_UP_CLIENT);
+       if (ret)
+               goto err_reset_lock;
 
        rtnl_unlock();
 
        return ret;
-}
+err_reset_lock:
+       rtnl_unlock();
+err_reset:
+       /* When VF reset failed, only the higher level reset asserted by PF
+        * can restore it, so re-initialize the command queue to receive
+        * this higher reset event.
+        */
+       hclgevf_cmd_init(hdev);
+       dev_err(&hdev->pdev->dev, "failed to reset VF\n");
 
-static int hclgevf_do_reset(struct hclgevf_dev *hdev)
-{
-       int status;
-       u8 respmsg;
+       return ret;
+}
 
-       status = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_RESET, 0, NULL,
-                                     0, false, &respmsg, sizeof(u8));
-       if (status)
-               dev_err(&hdev->pdev->dev,
-                       "VF reset request to PF failed(=%d)\n", status);
+static enum hnae3_reset_type hclgevf_get_reset_level(struct hclgevf_dev *hdev,
+                                                    unsigned long *addr)
+{
+       enum hnae3_reset_type rst_level = HNAE3_NONE_RESET;
+
+       /* return the highest priority reset level amongst all */
+       if (test_bit(HNAE3_VF_RESET, addr)) {
+               rst_level = HNAE3_VF_RESET;
+               clear_bit(HNAE3_VF_RESET, addr);
+               clear_bit(HNAE3_VF_PF_FUNC_RESET, addr);
+               clear_bit(HNAE3_VF_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_VF_FULL_RESET, addr)) {
+               rst_level = HNAE3_VF_FULL_RESET;
+               clear_bit(HNAE3_VF_FULL_RESET, addr);
+               clear_bit(HNAE3_VF_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_VF_PF_FUNC_RESET, addr)) {
+               rst_level = HNAE3_VF_PF_FUNC_RESET;
+               clear_bit(HNAE3_VF_PF_FUNC_RESET, addr);
+               clear_bit(HNAE3_VF_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_VF_FUNC_RESET, addr)) {
+               rst_level = HNAE3_VF_FUNC_RESET;
+               clear_bit(HNAE3_VF_FUNC_RESET, addr);
+       } else if (test_bit(HNAE3_FLR_RESET, addr)) {
+               rst_level = HNAE3_FLR_RESET;
+               clear_bit(HNAE3_FLR_RESET, addr);
+       }
 
-       return status;
+       return rst_level;
 }
 
 static void hclgevf_reset_event(struct pci_dev *pdev,
                                struct hnae3_handle *handle)
 {
-       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
+       struct hclgevf_dev *hdev = ae_dev->priv;
 
        dev_info(&hdev->pdev->dev, "received reset request from VF enet\n");
 
-       handle->reset_level = HNAE3_VF_RESET;
+       if (hdev->default_reset_request)
+               hdev->reset_level =
+                       hclgevf_get_reset_level(hdev,
+                                               &hdev->default_reset_request);
+       else
+               hdev->reset_level = HNAE3_VF_FUNC_RESET;
 
        /* reset of this VF requested */
        set_bit(HCLGEVF_RESET_REQUESTED, &hdev->reset_state);
        hclgevf_reset_task_schedule(hdev);
 
-       handle->last_reset_time = jiffies;
+       hdev->last_reset_time = jiffies;
+}
+
+static void hclgevf_set_def_reset_request(struct hnae3_ae_dev *ae_dev,
+                                         enum hnae3_reset_type rst_type)
+{
+       struct hclgevf_dev *hdev = ae_dev->priv;
+
+       set_bit(rst_type, &hdev->default_reset_request);
+}
+
+static void hclgevf_flr_prepare(struct hnae3_ae_dev *ae_dev)
+{
+#define HCLGEVF_FLR_WAIT_MS    100
+#define HCLGEVF_FLR_WAIT_CNT   50
+       struct hclgevf_dev *hdev = ae_dev->priv;
+       int cnt = 0;
+
+       clear_bit(HNAE3_FLR_DOWN, &hdev->flr_state);
+       clear_bit(HNAE3_FLR_DONE, &hdev->flr_state);
+       set_bit(HNAE3_FLR_RESET, &hdev->default_reset_request);
+       hclgevf_reset_event(hdev->pdev, NULL);
+
+       while (!test_bit(HNAE3_FLR_DOWN, &hdev->flr_state) &&
+              cnt++ < HCLGEVF_FLR_WAIT_CNT)
+               msleep(HCLGEVF_FLR_WAIT_MS);
+
+       if (!test_bit(HNAE3_FLR_DOWN, &hdev->flr_state))
+               dev_err(&hdev->pdev->dev,
+                       "flr wait down timeout: %d\n", cnt);
 }
 
 static u32 hclgevf_get_fw_version(struct hnae3_handle *handle)
@@ -1321,9 +1506,15 @@ static void hclgevf_reset_service_task(struct work_struct *work)
                 */
                hdev->reset_attempts = 0;
 
-               ret = hclgevf_reset(hdev);
-               if (ret)
-                       dev_err(&hdev->pdev->dev, "VF stack reset failed.\n");
+               hdev->last_reset_time = jiffies;
+               while ((hdev->reset_type =
+                       hclgevf_get_reset_level(hdev, &hdev->reset_pending))
+                      != HNAE3_NONE_RESET) {
+                       ret = hclgevf_reset(hdev);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "VF stack reset failed %d.\n", ret);
+               }
        } else if (test_and_clear_bit(HCLGEVF_RESET_REQUESTED,
                                      &hdev->reset_state)) {
                /* we could be here when either of below happens:
@@ -1352,19 +1543,17 @@ static void hclgevf_reset_service_task(struct work_struct *work)
                 */
                if (hdev->reset_attempts > 3) {
                        /* prepare for full reset of stack + pcie interface */
-                       hdev->nic.reset_level = HNAE3_VF_FULL_RESET;
+                       set_bit(HNAE3_VF_FULL_RESET, &hdev->reset_pending);
 
                        /* "defer" schedule the reset task again */
                        set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
                } else {
                        hdev->reset_attempts++;
 
-                       /* request PF for resetting this VF via mailbox */
-                       ret = hclgevf_do_reset(hdev);
-                       if (ret)
-                               dev_warn(&hdev->pdev->dev,
-                                        "VF rst fail, stack will call\n");
+                       set_bit(hdev->reset_level, &hdev->reset_pending);
+                       set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
                }
+               hclgevf_reset_task_schedule(hdev);
        }
 
        clear_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state);
@@ -1386,6 +1575,28 @@ static void hclgevf_mailbox_service_task(struct work_struct *work)
        clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state);
 }
 
+static void hclgevf_keep_alive_timer(struct timer_list *t)
+{
+       struct hclgevf_dev *hdev = from_timer(hdev, t, keep_alive_timer);
+
+       schedule_work(&hdev->keep_alive_task);
+       mod_timer(&hdev->keep_alive_timer, jiffies + 2 * HZ);
+}
+
+static void hclgevf_keep_alive_task(struct work_struct *work)
+{
+       struct hclgevf_dev *hdev;
+       u8 respmsg;
+       int ret;
+
+       hdev = container_of(work, struct hclgevf_dev, keep_alive_task);
+       ret = hclgevf_send_mbx_msg(hdev, HCLGE_MBX_KEEP_ALIVE, 0, NULL,
+                                  0, false, &respmsg, sizeof(u8));
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "VF sends keep alive cmd failed(=%d)\n", ret);
+}
+
 static void hclgevf_service_task(struct work_struct *work)
 {
        struct hclgevf_dev *hdev;
@@ -1407,24 +1618,37 @@ static void hclgevf_clear_event_cause(struct hclgevf_dev *hdev, u32 regclr)
        hclgevf_write_dev(&hdev->hw, HCLGEVF_VECTOR0_CMDQ_SRC_REG, regclr);
 }
 
-static bool hclgevf_check_event_cause(struct hclgevf_dev *hdev, u32 *clearval)
+static enum hclgevf_evt_cause hclgevf_check_evt_cause(struct hclgevf_dev *hdev,
+                                                     u32 *clearval)
 {
-       u32 cmdq_src_reg;
+       u32 cmdq_src_reg, rst_ing_reg;
 
        /* fetch the events from their corresponding regs */
        cmdq_src_reg = hclgevf_read_dev(&hdev->hw,
                                        HCLGEVF_VECTOR0_CMDQ_SRC_REG);
 
+       if (BIT(HCLGEVF_VECTOR0_RST_INT_B) & cmdq_src_reg) {
+               rst_ing_reg = hclgevf_read_dev(&hdev->hw, HCLGEVF_RST_ING);
+               dev_info(&hdev->pdev->dev,
+                        "receive reset interrupt 0x%x!\n", rst_ing_reg);
+               set_bit(HNAE3_VF_RESET, &hdev->reset_pending);
+               set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
+               set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state);
+               cmdq_src_reg &= ~BIT(HCLGEVF_VECTOR0_RST_INT_B);
+               *clearval = cmdq_src_reg;
+               return HCLGEVF_VECTOR0_EVENT_RST;
+       }
+
        /* check for vector0 mailbox(=CMDQ RX) event source */
        if (BIT(HCLGEVF_VECTOR0_RX_CMDQ_INT_B) & cmdq_src_reg) {
                cmdq_src_reg &= ~BIT(HCLGEVF_VECTOR0_RX_CMDQ_INT_B);
                *clearval = cmdq_src_reg;
-               return true;
+               return HCLGEVF_VECTOR0_EVENT_MBX;
        }
 
        dev_dbg(&hdev->pdev->dev, "vector 0 interrupt from unknown source\n");
 
-       return false;
+       return HCLGEVF_VECTOR0_EVENT_OTHER;
 }
 
 static void hclgevf_enable_vector(struct hclgevf_misc_vector *vector, bool en)
@@ -1434,19 +1658,28 @@ static void hclgevf_enable_vector(struct hclgevf_misc_vector *vector, bool en)
 
 static irqreturn_t hclgevf_misc_irq_handle(int irq, void *data)
 {
+       enum hclgevf_evt_cause event_cause;
        struct hclgevf_dev *hdev = data;
        u32 clearval;
 
        hclgevf_enable_vector(&hdev->misc_vector, false);
-       if (!hclgevf_check_event_cause(hdev, &clearval))
-               goto skip_sched;
-
-       hclgevf_mbx_handler(hdev);
+       event_cause = hclgevf_check_evt_cause(hdev, &clearval);
 
-       hclgevf_clear_event_cause(hdev, clearval);
+       switch (event_cause) {
+       case HCLGEVF_VECTOR0_EVENT_RST:
+               hclgevf_reset_task_schedule(hdev);
+               break;
+       case HCLGEVF_VECTOR0_EVENT_MBX:
+               hclgevf_mbx_handler(hdev);
+               break;
+       default:
+               break;
+       }
 
-skip_sched:
-       hclgevf_enable_vector(&hdev->misc_vector, true);
+       if (event_cause != HCLGEVF_VECTOR0_EVENT_OTHER) {
+               hclgevf_clear_event_cause(hdev, clearval);
+               hclgevf_enable_vector(&hdev->misc_vector, true);
+       }
 
        return IRQ_HANDLED;
 }
@@ -1504,6 +1737,29 @@ static int hclgevf_init_roce_base_info(struct hclgevf_dev *hdev)
        return 0;
 }
 
+static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en)
+{
+       struct hclgevf_cfg_gro_status_cmd *req;
+       struct hclgevf_desc desc;
+       int ret;
+
+       if (!hnae3_dev_gro_supported(hdev))
+               return 0;
+
+       hclgevf_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_GRO_GENERIC_CONFIG,
+                                    false);
+       req = (struct hclgevf_cfg_gro_status_cmd *)desc.data;
+
+       req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+       ret = hclgevf_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "VF GRO hardware config cmd failed, ret = %d.\n", ret);
+
+       return ret;
+}
+
 static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)
 {
        struct hclgevf_rss_cfg *rss_cfg = &hdev->rss_cfg;
@@ -1566,21 +1822,7 @@ static int hclgevf_init_vlan_config(struct hclgevf_dev *hdev)
 
 static int hclgevf_ae_start(struct hnae3_handle *handle)
 {
-       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
-       int i, queue_id;
-
-       for (i = 0; i < kinfo->num_tqps; i++) {
-               /* ring enable */
-               queue_id = hclgevf_get_queue_id(kinfo->tqp[i]);
-               if (queue_id < 0) {
-                       dev_warn(&hdev->pdev->dev,
-                                "Get invalid queue id, ignore it\n");
-                       continue;
-               }
-
-               hclgevf_tqp_enable(hdev, queue_id, 0, true);
-       }
 
        /* reset tqp stats */
        hclgevf_reset_tqp_stats(handle);
@@ -1595,24 +1837,10 @@ static int hclgevf_ae_start(struct hnae3_handle *handle)
 
 static void hclgevf_ae_stop(struct hnae3_handle *handle)
 {
-       struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
-       int i, queue_id;
 
        set_bit(HCLGEVF_STATE_DOWN, &hdev->state);
 
-       for (i = 0; i < kinfo->num_tqps; i++) {
-               /* Ring disable */
-               queue_id = hclgevf_get_queue_id(kinfo->tqp[i]);
-               if (queue_id < 0) {
-                       dev_warn(&hdev->pdev->dev,
-                                "Get invalid queue id, ignore it\n");
-                       continue;
-               }
-
-               hclgevf_tqp_enable(hdev, queue_id, 0, false);
-       }
-
        /* reset tqp stats */
        hclgevf_reset_tqp_stats(handle);
        del_timer_sync(&hdev->service_timer);
@@ -1621,12 +1849,40 @@ static void hclgevf_ae_stop(struct hnae3_handle *handle)
        hclgevf_update_link_status(hdev, 0);
 }
 
-static void hclgevf_state_init(struct hclgevf_dev *hdev)
+static int hclgevf_set_alive(struct hnae3_handle *handle, bool alive)
 {
-       /* if this is on going reset then skip this initialization */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               return;
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       u8 msg_data;
+
+       msg_data = alive ? 1 : 0;
+       return hclgevf_send_mbx_msg(hdev, HCLGE_MBX_SET_ALIVE,
+                                   0, &msg_data, 1, false, NULL, 0);
+}
+
+static int hclgevf_client_start(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       mod_timer(&hdev->keep_alive_timer, jiffies + 2 * HZ);
+       return hclgevf_set_alive(handle, true);
+}
+
+static void hclgevf_client_stop(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       int ret;
+
+       ret = hclgevf_set_alive(handle, false);
+       if (ret)
+               dev_warn(&hdev->pdev->dev,
+                        "%s failed %d\n", __func__, ret);
+
+       del_timer_sync(&hdev->keep_alive_timer);
+       cancel_work_sync(&hdev->keep_alive_task);
+}
 
+static void hclgevf_state_init(struct hclgevf_dev *hdev)
+{
        /* setup tasks for the MBX */
        INIT_WORK(&hdev->mbx_service_task, hclgevf_mailbox_service_task);
        clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state);
@@ -1668,10 +1924,6 @@ static int hclgevf_init_msi(struct hclgevf_dev *hdev)
        int vectors;
        int i;
 
-       /* if this is on going reset then skip this initialization */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               return 0;
-
        if (hnae3_get_bit(hdev->ae_dev->flag, HNAE3_DEV_SUPPORT_ROCE_B))
                vectors = pci_alloc_irq_vectors(pdev,
                                                hdev->roce_base_msix_offset + 1,
@@ -1710,6 +1962,7 @@ static int hclgevf_init_msi(struct hclgevf_dev *hdev)
        hdev->vector_irq = devm_kcalloc(&pdev->dev, hdev->num_msi,
                                        sizeof(int), GFP_KERNEL);
        if (!hdev->vector_irq) {
+               devm_kfree(&pdev->dev, hdev->vector_status);
                pci_free_irq_vectors(pdev);
                return -ENOMEM;
        }
@@ -1721,6 +1974,8 @@ static void hclgevf_uninit_msi(struct hclgevf_dev *hdev)
 {
        struct pci_dev *pdev = hdev->pdev;
 
+       devm_kfree(&pdev->dev, hdev->vector_status);
+       devm_kfree(&pdev->dev, hdev->vector_irq);
        pci_free_irq_vectors(pdev);
 }
 
@@ -1728,10 +1983,6 @@ static int hclgevf_misc_irq_init(struct hclgevf_dev *hdev)
 {
        int ret = 0;
 
-       /* if this is on going reset then skip this initialization */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               return 0;
-
        hclgevf_get_misc_vector(hdev);
 
        ret = request_irq(hdev->misc_vector.vector_irq, hclgevf_misc_irq_handle,
@@ -1861,14 +2112,6 @@ static int hclgevf_pci_init(struct hclgevf_dev *hdev)
        struct hclgevf_hw *hw;
        int ret;
 
-       /* check if we need to skip initialization of pci. This will happen if
-        * device is undergoing VF reset. Otherwise, we would need to
-        * re-initialize pci interface again i.e. when device is not going
-        * through *any* reset or actually undergoing full reset.
-        */
-       if (hclgevf_dev_ongoing_reset(hdev))
-               return 0;
-
        ret = pci_enable_device(pdev);
        if (ret) {
                dev_err(&pdev->dev, "failed to enable PCI device\n");
@@ -1957,23 +2200,98 @@ static int hclgevf_query_vf_resource(struct hclgevf_dev *hdev)
        return 0;
 }
 
-static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
+static int hclgevf_pci_reset(struct hclgevf_dev *hdev)
+{
+       struct pci_dev *pdev = hdev->pdev;
+       int ret = 0;
+
+       if (hdev->reset_type == HNAE3_VF_FULL_RESET &&
+           test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
+               hclgevf_misc_irq_uninit(hdev);
+               hclgevf_uninit_msi(hdev);
+               clear_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state);
+       }
+
+       if (!test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
+               pci_set_master(pdev);
+               ret = hclgevf_init_msi(hdev);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "failed(%d) to init MSI/MSI-X\n", ret);
+                       return ret;
+               }
+
+               ret = hclgevf_misc_irq_init(hdev);
+               if (ret) {
+                       hclgevf_uninit_msi(hdev);
+                       dev_err(&pdev->dev, "failed(%d) to init Misc IRQ(vector0)\n",
+                               ret);
+                       return ret;
+               }
+
+               set_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state);
+       }
+
+       return ret;
+}
+
+static int hclgevf_reset_hdev(struct hclgevf_dev *hdev)
 {
        struct pci_dev *pdev = hdev->pdev;
        int ret;
 
-       /* check if device is on-going full reset(i.e. pcie as well) */
-       if (hclgevf_dev_ongoing_full_reset(hdev)) {
-               dev_warn(&pdev->dev, "device is going full reset\n");
-               hclgevf_uninit_hdev(hdev);
+       ret = hclgevf_pci_reset(hdev);
+       if (ret) {
+               dev_err(&pdev->dev, "pci reset failed %d\n", ret);
+               return ret;
+       }
+
+       ret = hclgevf_cmd_init(hdev);
+       if (ret) {
+               dev_err(&pdev->dev, "cmd failed %d\n", ret);
+               return ret;
        }
 
+       ret = hclgevf_rss_init_hw(hdev);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed(%d) to initialize RSS\n", ret);
+               return ret;
+       }
+
+       ret = hclgevf_config_gro(hdev, true);
+       if (ret)
+               return ret;
+
+       ret = hclgevf_init_vlan_config(hdev);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed(%d) to initialize VLAN config\n", ret);
+               return ret;
+       }
+
+       dev_info(&hdev->pdev->dev, "Reset done\n");
+
+       return 0;
+}
+
+static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
+{
+       struct pci_dev *pdev = hdev->pdev;
+       int ret;
+
        ret = hclgevf_pci_init(hdev);
        if (ret) {
                dev_err(&pdev->dev, "PCI initialization failed\n");
                return ret;
        }
 
+       ret = hclgevf_cmd_queue_init(hdev);
+       if (ret) {
+               dev_err(&pdev->dev, "Cmd queue init failed: %d\n", ret);
+               goto err_cmd_queue_init;
+       }
+
        ret = hclgevf_cmd_init(hdev);
        if (ret)
                goto err_cmd_init;
@@ -1983,16 +2301,17 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
        if (ret) {
                dev_err(&hdev->pdev->dev,
                        "Query vf status error, ret = %d.\n", ret);
-               goto err_query_vf;
+               goto err_cmd_init;
        }
 
        ret = hclgevf_init_msi(hdev);
        if (ret) {
                dev_err(&pdev->dev, "failed(%d) to init MSI/MSI-X\n", ret);
-               goto err_query_vf;
+               goto err_cmd_init;
        }
 
        hclgevf_state_init(hdev);
+       hdev->reset_level = HNAE3_VF_FUNC_RESET;
 
        ret = hclgevf_misc_irq_init(hdev);
        if (ret) {
@@ -2001,6 +2320,8 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_misc_irq_init;
        }
 
+       set_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state);
+
        ret = hclgevf_configure(hdev);
        if (ret) {
                dev_err(&pdev->dev, "failed(%d) to fetch configuration\n", ret);
@@ -2019,6 +2340,10 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_config;
        }
 
+       ret = hclgevf_config_gro(hdev, true);
+       if (ret)
+               goto err_config;
+
        /* Initialize RSS for this VF */
        ret = hclgevf_rss_init_hw(hdev);
        if (ret) {
@@ -2034,6 +2359,7 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_config;
        }
 
+       hdev->last_reset_time = jiffies;
        pr_info("finished initializing %s driver\n", HCLGEVF_DRIVER_NAME);
 
        return 0;
@@ -2043,25 +2369,31 @@ err_config:
 err_misc_irq_init:
        hclgevf_state_uninit(hdev);
        hclgevf_uninit_msi(hdev);
-err_query_vf:
-       hclgevf_cmd_uninit(hdev);
 err_cmd_init:
+       hclgevf_cmd_uninit(hdev);
+err_cmd_queue_init:
        hclgevf_pci_uninit(hdev);
+       clear_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state);
        return ret;
 }
 
 static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev)
 {
        hclgevf_state_uninit(hdev);
-       hclgevf_misc_irq_uninit(hdev);
+
+       if (test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
+               hclgevf_misc_irq_uninit(hdev);
+               hclgevf_uninit_msi(hdev);
+               hclgevf_pci_uninit(hdev);
+       }
+
        hclgevf_cmd_uninit(hdev);
-       hclgevf_uninit_msi(hdev);
-       hclgevf_pci_uninit(hdev);
 }
 
 static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        struct pci_dev *pdev = ae_dev->pdev;
+       struct hclgevf_dev *hdev;
        int ret;
 
        ret = hclgevf_alloc_hdev(ae_dev);
@@ -2071,10 +2403,16 @@ static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        }
 
        ret = hclgevf_init_hdev(ae_dev->priv);
-       if (ret)
+       if (ret) {
                dev_err(&pdev->dev, "hclge device initialization failed\n");
+               return ret;
+       }
 
-       return ret;
+       hdev = ae_dev->priv;
+       timer_setup(&hdev->keep_alive_timer, hclgevf_keep_alive_timer, 0);
+       INIT_WORK(&hdev->keep_alive_task, hclgevf_keep_alive_task);
+
+       return 0;
 }
 
 static void hclgevf_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
@@ -2151,6 +2489,13 @@ void hclgevf_update_speed_duplex(struct hclgevf_dev *hdev, u32 speed,
        hdev->hw.mac.duplex = duplex;
 }
 
+static int hclgevf_gro_en(struct hnae3_handle *handle, int enable)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return hclgevf_config_gro(hdev, enable);
+}
+
 static void hclgevf_get_media_type(struct hnae3_handle *handle,
                                  u8 *media_type)
 {
@@ -2159,13 +2504,104 @@ static void hclgevf_get_media_type(struct hnae3_handle *handle,
                *media_type = hdev->hw.mac.media_type;
 }
 
+static bool hclgevf_get_hw_reset_stat(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return !!hclgevf_read_dev(&hdev->hw, HCLGEVF_RST_ING);
+}
+
+static bool hclgevf_ae_dev_resetting(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state);
+}
+
+static unsigned long hclgevf_ae_dev_reset_cnt(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       return hdev->reset_count;
+}
+
+#define MAX_SEPARATE_NUM       4
+#define SEPARATOR_VALUE                0xFFFFFFFF
+#define REG_NUM_PER_LINE       4
+#define REG_LEN_PER_LINE       (REG_NUM_PER_LINE * sizeof(u32))
+
+static int hclgevf_get_regs_len(struct hnae3_handle *handle)
+{
+       int cmdq_lines, common_lines, ring_lines, tqp_intr_lines;
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       cmdq_lines = sizeof(cmdq_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       common_lines = sizeof(common_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       ring_lines = sizeof(ring_reg_addr_list) / REG_LEN_PER_LINE + 1;
+       tqp_intr_lines = sizeof(tqp_intr_reg_addr_list) / REG_LEN_PER_LINE + 1;
+
+       return (cmdq_lines + common_lines + ring_lines * hdev->num_tqps +
+               tqp_intr_lines * (hdev->num_msi_used - 1)) * REG_LEN_PER_LINE;
+}
+
+static void hclgevf_get_regs(struct hnae3_handle *handle, u32 *version,
+                            void *data)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       int i, j, reg_um, separator_num;
+       u32 *reg = data;
+
+       *version = hdev->fw_version;
+
+       /* fetching per-VF registers values from VF PCIe register space */
+       reg_um = sizeof(cmdq_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclgevf_read_dev(&hdev->hw, cmdq_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(common_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (i = 0; i < reg_um; i++)
+               *reg++ = hclgevf_read_dev(&hdev->hw, common_reg_addr_list[i]);
+       for (i = 0; i < separator_num; i++)
+               *reg++ = SEPARATOR_VALUE;
+
+       reg_um = sizeof(ring_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_tqps; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclgevf_read_dev(&hdev->hw,
+                                                 ring_reg_addr_list[i] +
+                                                 0x200 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+
+       reg_um = sizeof(tqp_intr_reg_addr_list) / sizeof(u32);
+       separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE;
+       for (j = 0; j < hdev->num_msi_used - 1; j++) {
+               for (i = 0; i < reg_um; i++)
+                       *reg++ = hclgevf_read_dev(&hdev->hw,
+                                                 tqp_intr_reg_addr_list[i] +
+                                                 4 * j);
+               for (i = 0; i < separator_num; i++)
+                       *reg++ = SEPARATOR_VALUE;
+       }
+}
+
 static const struct hnae3_ae_ops hclgevf_ops = {
        .init_ae_dev = hclgevf_init_ae_dev,
        .uninit_ae_dev = hclgevf_uninit_ae_dev,
+       .flr_prepare = hclgevf_flr_prepare,
+       .flr_done = hclgevf_flr_done,
        .init_client_instance = hclgevf_init_client_instance,
        .uninit_client_instance = hclgevf_uninit_client_instance,
        .start = hclgevf_ae_start,
        .stop = hclgevf_ae_stop,
+       .client_start = hclgevf_client_start,
+       .client_stop = hclgevf_client_stop,
        .map_ring_to_vector = hclgevf_map_ring_to_vector,
        .unmap_ring_from_vector = hclgevf_unmap_ring_from_vector,
        .get_vector = hclgevf_get_vector,
@@ -2193,11 +2629,19 @@ static const struct hnae3_ae_ops hclgevf_ops = {
        .set_vlan_filter = hclgevf_set_vlan_filter,
        .enable_hw_strip_rxvtag = hclgevf_en_hw_strip_rxvtag,
        .reset_event = hclgevf_reset_event,
+       .set_default_reset_request = hclgevf_set_def_reset_request,
        .get_channels = hclgevf_get_channels,
        .get_tqps_and_rss_info = hclgevf_get_tqps_and_rss_info,
+       .get_regs_len = hclgevf_get_regs_len,
+       .get_regs = hclgevf_get_regs,
        .get_status = hclgevf_get_status,
        .get_ksettings_an_result = hclgevf_get_ksettings_an_result,
        .get_media_type = hclgevf_get_media_type,
+       .get_hw_reset_stat = hclgevf_get_hw_reset_stat,
+       .ae_dev_resetting = hclgevf_ae_dev_resetting,
+       .ae_dev_reset_cnt = hclgevf_ae_dev_reset_cnt,
+       .set_gro_en = hclgevf_gro_en,
+       .set_mtu = hclgevf_set_mtu,
 };
 
 static struct hnae3_ae_algo ae_algovf = {
index aed241e..787bc06 100644 (file)
 #define HCLGEVF_VECTOR_REG_OFFSET      0x4
 #define HCLGEVF_VECTOR_VF_OFFSET               0x100000
 
+/* bar registers for cmdq */
+#define HCLGEVF_CMDQ_TX_ADDR_L_REG             0x27000
+#define HCLGEVF_CMDQ_TX_ADDR_H_REG             0x27004
+#define HCLGEVF_CMDQ_TX_DEPTH_REG              0x27008
+#define HCLGEVF_CMDQ_TX_TAIL_REG               0x27010
+#define HCLGEVF_CMDQ_TX_HEAD_REG               0x27014
+#define HCLGEVF_CMDQ_RX_ADDR_L_REG             0x27018
+#define HCLGEVF_CMDQ_RX_ADDR_H_REG             0x2701C
+#define HCLGEVF_CMDQ_RX_DEPTH_REG              0x27020
+#define HCLGEVF_CMDQ_RX_TAIL_REG               0x27024
+#define HCLGEVF_CMDQ_RX_HEAD_REG               0x27028
+#define HCLGEVF_CMDQ_INTR_SRC_REG              0x27100
+#define HCLGEVF_CMDQ_INTR_STS_REG              0x27104
+#define HCLGEVF_CMDQ_INTR_EN_REG               0x27108
+#define HCLGEVF_CMDQ_INTR_GEN_REG              0x2710C
+
+/* bar registers for common func */
+#define HCLGEVF_GRO_EN_REG                     0x28000
+
+/* bar registers for rcb */
+#define HCLGEVF_RING_RX_ADDR_L_REG             0x80000
+#define HCLGEVF_RING_RX_ADDR_H_REG             0x80004
+#define HCLGEVF_RING_RX_BD_NUM_REG             0x80008
+#define HCLGEVF_RING_RX_BD_LENGTH_REG          0x8000C
+#define HCLGEVF_RING_RX_MERGE_EN_REG           0x80014
+#define HCLGEVF_RING_RX_TAIL_REG               0x80018
+#define HCLGEVF_RING_RX_HEAD_REG               0x8001C
+#define HCLGEVF_RING_RX_FBD_NUM_REG            0x80020
+#define HCLGEVF_RING_RX_OFFSET_REG             0x80024
+#define HCLGEVF_RING_RX_FBD_OFFSET_REG         0x80028
+#define HCLGEVF_RING_RX_STASH_REG              0x80030
+#define HCLGEVF_RING_RX_BD_ERR_REG             0x80034
+#define HCLGEVF_RING_TX_ADDR_L_REG             0x80040
+#define HCLGEVF_RING_TX_ADDR_H_REG             0x80044
+#define HCLGEVF_RING_TX_BD_NUM_REG             0x80048
+#define HCLGEVF_RING_TX_PRIORITY_REG           0x8004C
+#define HCLGEVF_RING_TX_TC_REG                 0x80050
+#define HCLGEVF_RING_TX_MERGE_EN_REG           0x80054
+#define HCLGEVF_RING_TX_TAIL_REG               0x80058
+#define HCLGEVF_RING_TX_HEAD_REG               0x8005C
+#define HCLGEVF_RING_TX_FBD_NUM_REG            0x80060
+#define HCLGEVF_RING_TX_OFFSET_REG             0x80064
+#define HCLGEVF_RING_TX_EBD_NUM_REG            0x80068
+#define HCLGEVF_RING_TX_EBD_OFFSET_REG         0x80070
+#define HCLGEVF_RING_TX_BD_ERR_REG             0x80074
+#define HCLGEVF_RING_EN_REG                    0x80090
+
+/* bar registers for tqp interrupt */
+#define HCLGEVF_TQP_INTR_CTRL_REG              0x20000
+#define HCLGEVF_TQP_INTR_GL0_REG               0x20100
+#define HCLGEVF_TQP_INTR_GL1_REG               0x20200
+#define HCLGEVF_TQP_INTR_GL2_REG               0x20300
+#define HCLGEVF_TQP_INTR_RL_REG                        0x20900
+
 /* Vector0 interrupt CMDQ event source register(RW) */
 #define HCLGEVF_VECTOR0_CMDQ_SRC_REG   0x27100
 /* CMDQ register bits for RX event(=MBX event) */
 #define HCLGEVF_VECTOR0_RX_CMDQ_INT_B  1
+/* RST register bits for RESET event */
+#define HCLGEVF_VECTOR0_RST_INT_B      2
 
 #define HCLGEVF_TQP_RESET_TRY_TIMES    10
 /* Reset related Registers */
-#define HCLGEVF_FUN_RST_ING            0x20C00
-#define HCLGEVF_FUN_RST_ING_B          0
+#define HCLGEVF_RST_ING                        0x20C00
+#define HCLGEVF_FUN_RST_ING_BIT                BIT(0)
+#define HCLGEVF_GLOBAL_RST_ING_BIT     BIT(5)
+#define HCLGEVF_CORE_RST_ING_BIT       BIT(6)
+#define HCLGEVF_IMP_RST_ING_BIT                BIT(7)
+#define HCLGEVF_RST_ING_BITS \
+       (HCLGEVF_FUN_RST_ING_BIT | HCLGEVF_GLOBAL_RST_ING_BIT | \
+        HCLGEVF_CORE_RST_ING_BIT | HCLGEVF_IMP_RST_ING_BIT)
 
 #define HCLGEVF_RSS_IND_TBL_SIZE               512
 #define HCLGEVF_RSS_SET_BITMAP_MSK     0xffff
 #define HCLGEVF_S_IP_BIT               BIT(3)
 #define HCLGEVF_V_TAG_BIT              BIT(4)
 
+enum hclgevf_evt_cause {
+       HCLGEVF_VECTOR0_EVENT_RST,
+       HCLGEVF_VECTOR0_EVENT_MBX,
+       HCLGEVF_VECTOR0_EVENT_OTHER,
+};
+
 /* states of hclgevf device & tasks */
 enum hclgevf_states {
        /* device states */
        HCLGEVF_STATE_DOWN,
        HCLGEVF_STATE_DISABLED,
+       HCLGEVF_STATE_IRQ_INITED,
        /* task states */
        HCLGEVF_STATE_SERVICE_SCHED,
        HCLGEVF_STATE_RST_SERVICE_SCHED,
        HCLGEVF_STATE_RST_HANDLING,
        HCLGEVF_STATE_MBX_SERVICE_SCHED,
        HCLGEVF_STATE_MBX_HANDLING,
+       HCLGEVF_STATE_CMD_DISABLE,
 };
 
 #define HCLGEVF_MPF_ENBALE 1
@@ -145,10 +215,17 @@ struct hclgevf_dev {
        struct hclgevf_misc_vector misc_vector;
        struct hclgevf_rss_cfg rss_cfg;
        unsigned long state;
+       unsigned long flr_state;
+       unsigned long default_reset_request;
+       unsigned long last_reset_time;
+       enum hnae3_reset_type reset_level;
+       unsigned long reset_pending;
+       enum hnae3_reset_type reset_type;
 
 #define HCLGEVF_RESET_REQUESTED                0
 #define HCLGEVF_RESET_PENDING          1
        unsigned long reset_state;      /* requested, pending */
+       unsigned long reset_count;      /* the number of reset has been done */
        u32 reset_attempts;
 
        u32 fw_version;
@@ -178,7 +255,9 @@ struct hclgevf_dev {
        struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */
 
        struct timer_list service_timer;
+       struct timer_list keep_alive_timer;
        struct work_struct service_task;
+       struct work_struct keep_alive_task;
        struct work_struct rst_service_task;
        struct work_struct mbx_service_task;
 
@@ -192,18 +271,9 @@ struct hclgevf_dev {
        u32 flag;
 };
 
-static inline bool hclgevf_dev_ongoing_reset(struct hclgevf_dev *hdev)
-{
-       return (hdev &&
-               (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) &&
-               (hdev->nic.reset_level == HNAE3_VF_RESET));
-}
-
-static inline bool hclgevf_dev_ongoing_full_reset(struct hclgevf_dev *hdev)
+static inline bool hclgevf_is_reset_pending(struct hclgevf_dev *hdev)
 {
-       return (hdev &&
-               (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) &&
-               (hdev->nic.reset_level == HNAE3_VF_FULL_RESET));
+       return !!hdev->reset_pending;
 }
 
 int hclgevf_send_mbx_msg(struct hclgevf_dev *hdev, u16 code, u16 subcode,
index e9d5a4f..ef9c8e6 100644 (file)
@@ -40,6 +40,9 @@ static int hclgevf_get_mbx_resp(struct hclgevf_dev *hdev, u16 code0, u16 code1,
        }
 
        while ((!hdev->mbx_resp.received_resp) && (i < HCLGEVF_MAX_TRY_TIMES)) {
+               if (test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state))
+                       return -EIO;
+
                udelay(HCLGEVF_SLEEP_USCOEND);
                i++;
        }
@@ -148,6 +151,11 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
        crq = &hdev->hw.cmq.crq;
 
        while (!hclgevf_cmd_crq_empty(&hdev->hw)) {
+               if (test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state)) {
+                       dev_info(&hdev->pdev->dev, "vf crq need init\n");
+                       return;
+               }
+
                desc = &crq->desc[crq->next_to_use];
                req = (struct hclge_mbx_pf_to_vf_cmd *)desc->data;
 
@@ -233,6 +241,7 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
 
 void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
 {
+       enum hnae3_reset_type reset_type;
        u16 link_status;
        u16 *msg_q;
        u8 duplex;
@@ -248,6 +257,12 @@ void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
 
        /* process all the async queue messages */
        while (tail != hdev->arq.head) {
+               if (test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state)) {
+                       dev_info(&hdev->pdev->dev,
+                                "vf crq need init in async\n");
+                       return;
+               }
+
                msg_q = hdev->arq.msg_q[hdev->arq.head];
 
                switch (msg_q[0]) {
@@ -267,7 +282,8 @@ void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev)
                         * has been completely reset. After this stack should
                         * eventually be re-initialized.
                         */
-                       hdev->nic.reset_level = HNAE3_VF_RESET;
+                       reset_type = le16_to_cpu(msg_q[1]);
+                       set_bit(reset_type, &hdev->reset_pending);
                        set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state);
                        hclgevf_reset_task_schedule(hdev);
 
index 097b550..d1a7d25 100644 (file)
@@ -50,6 +50,8 @@ enum hinic_port_cmd {
 
        HINIC_PORT_CMD_GET_LINK_STATE   = 24,
 
+       HINIC_PORT_CMD_SET_RX_CSUM      = 26,
+
        HINIC_PORT_CMD_SET_PORT_STATE   = 41,
 
        HINIC_PORT_CMD_FWCTXT_INIT      = 69,
index f92f1bf..1dfa7eb 100644 (file)
                        ((void *)((cmdq_pages)->shadow_page_vaddr) \
                                + (wq)->block_idx * CMDQ_BLOCK_SIZE)
 
-#define WQE_PAGE_OFF(wq, idx)   (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \
-                                       (wq)->wqebb_size)
-
-#define WQE_PAGE_NUM(wq, idx)   (((idx) / ((wq)->num_wqebbs_per_page)) \
-                                       & ((wq)->num_q_pages - 1))
-
 #define WQ_PAGE_ADDR(wq, idx)           \
                        ((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)])
 
                (((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \
                        / (wq)->max_wqe_size)
 
+static inline int WQE_PAGE_OFF(struct hinic_wq *wq, u16 idx)
+{
+       return (((idx) & ((wq)->num_wqebbs_per_page - 1))
+               << (wq)->wqebb_size_shift);
+}
+
+static inline int WQE_PAGE_NUM(struct hinic_wq *wq, u16 idx)
+{
+       return (((idx) >> ((wq)->wqebbs_per_page_shift))
+               & ((wq)->num_q_pages - 1));
+}
 /**
  * queue_alloc_page - allocate page for Queue
  * @hwif: HW interface for allocating DMA
@@ -513,10 +518,11 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
        struct hinic_hwif *hwif = wqs->hwif;
        struct pci_dev *pdev = hwif->pdev;
        u16 num_wqebbs_per_page;
+       u16 wqebb_size_shift;
        int err;
 
-       if (wqebb_size == 0) {
-               dev_err(&pdev->dev, "wqebb_size must be > 0\n");
+       if (!is_power_of_2(wqebb_size)) {
+               dev_err(&pdev->dev, "wqebb_size must be power of 2\n");
                return -EINVAL;
        }
 
@@ -530,9 +536,11 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
                return -EINVAL;
        }
 
-       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+       wqebb_size_shift = ilog2(wqebb_size);
+       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+                               >> wqebb_size_shift;
 
-       if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
+       if (!is_power_of_2(num_wqebbs_per_page)) {
                dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
                return -EINVAL;
        }
@@ -550,7 +558,8 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
        wq->q_depth = q_depth;
        wq->max_wqe_size = max_wqe_size;
        wq->num_wqebbs_per_page = num_wqebbs_per_page;
-
+       wq->wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
+       wq->wqebb_size_shift = wqebb_size_shift;
        wq->block_vaddr = WQ_BASE_VADDR(wqs, wq);
        wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq);
        wq->block_paddr = WQ_BASE_PADDR(wqs, wq);
@@ -604,11 +613,13 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                         u16 q_depth, u16 max_wqe_size)
 {
        struct pci_dev *pdev = hwif->pdev;
+       u16 num_wqebbs_per_page_shift;
        u16 num_wqebbs_per_page;
+       u16 wqebb_size_shift;
        int i, j, err = -ENOMEM;
 
-       if (wqebb_size == 0) {
-               dev_err(&pdev->dev, "wqebb_size must be > 0\n");
+       if (!is_power_of_2(wqebb_size)) {
+               dev_err(&pdev->dev, "wqebb_size must be power of 2\n");
                return -EINVAL;
        }
 
@@ -622,9 +633,11 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                return -EINVAL;
        }
 
-       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+       wqebb_size_shift = ilog2(wqebb_size);
+       num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+                               >> wqebb_size_shift;
 
-       if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
+       if (!is_power_of_2(num_wqebbs_per_page)) {
                dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
                return -EINVAL;
        }
@@ -636,6 +649,7 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                dev_err(&pdev->dev, "Failed to allocate CMDQ page\n");
                return err;
        }
+       num_wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
 
        for (i = 0; i < cmdq_blocks; i++) {
                wq[i].hwif = hwif;
@@ -647,7 +661,8 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                wq[i].q_depth = q_depth;
                wq[i].max_wqe_size = max_wqe_size;
                wq[i].num_wqebbs_per_page = num_wqebbs_per_page;
-
+               wq[i].wqebbs_per_page_shift = num_wqebbs_per_page_shift;
+               wq[i].wqebb_size_shift = wqebb_size_shift;
                wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]);
                wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]);
                wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]);
@@ -741,7 +756,7 @@ struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size,
 
        *prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx));
 
-       num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) >> wq->wqebb_size_shift;
 
        if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) {
                atomic_add(num_wqebbs, &wq->delta);
@@ -795,7 +810,8 @@ void hinic_return_wqe(struct hinic_wq *wq, unsigned int wqe_size)
  **/
 void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
 {
-       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+                       >> wq->wqebb_size_shift;
 
        atomic_add(num_wqebbs, &wq->cons_idx);
 
@@ -813,7 +829,8 @@ void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
 struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size,
                                    u16 *cons_idx)
 {
-       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+       int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+                       >> wq->wqebb_size_shift;
        u16 curr_cons_idx, end_cons_idx;
        int curr_pg, end_pg;
 
index 9b66545..0a936cd 100644 (file)
@@ -39,7 +39,8 @@ struct hinic_wq {
        u16             q_depth;
        u16             max_wqe_size;
        u16             num_wqebbs_per_page;
-
+       u16             wqebbs_per_page_shift;
+       u16             wqebb_size_shift;
        /* The addresses are 64 bit in the HW */
        u64             block_paddr;
        void            **shadow_block_vaddr;
index 9754d6e..1389415 100644 (file)
 
 #define HINIC_RQ_CQE_STATUS_RXDONE_MASK         0x1
 
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_SHIFT     0
+
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_MASK      0xFFFFU
+
 #define HINIC_RQ_CQE_STATUS_GET(val, member)    \
                (((val) >> HINIC_RQ_CQE_STATUS_##member##_SHIFT) & \
                 HINIC_RQ_CQE_STATUS_##member##_MASK)
index fdf2bdb..6d48dc6 100644 (file)
@@ -600,9 +600,6 @@ static int add_mac_addr(struct net_device *netdev, const u8 *addr)
        u16 vid = 0;
        int err;
 
-       if (!is_valid_ether_addr(addr))
-               return -EADDRNOTAVAIL;
-
        netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n",
                   addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
 
@@ -726,6 +723,7 @@ static void set_rx_mode(struct work_struct *work)
 {
        struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
        struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
+       struct netdev_hw_addr *ha;
 
        netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
 
@@ -733,6 +731,9 @@ static void set_rx_mode(struct work_struct *work)
 
        __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
        __dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
+
+       netdev_for_each_mc_addr(ha, nic_dev->netdev)
+               add_mac_addr(nic_dev->netdev, ha->addr);
 }
 
 static void hinic_set_rx_mode(struct net_device *netdev)
@@ -806,7 +807,8 @@ static const struct net_device_ops hinic_netdev_ops = {
 static void netdev_features_init(struct net_device *netdev)
 {
        netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
-                             NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
+                             NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
+                             NETIF_F_RXCSUM;
 
        netdev->vlan_features = netdev->hw_features;
 
@@ -869,12 +871,16 @@ static int set_features(struct hinic_dev *nic_dev,
                        netdev_features_t features, bool force_change)
 {
        netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
+       u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
        int err = 0;
 
        if (changed & NETIF_F_TSO)
                err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
                                         HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
 
+       if (changed & NETIF_F_RXCSUM)
+               err = hinic_set_rx_csum_offload(nic_dev, csum_en);
+
        return err;
 }
 
index 7575a7d..122c935 100644 (file)
@@ -409,3 +409,33 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state)
 
        return 0;
 }
+
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
+{
+       struct hinic_checksum_offload rx_csum_cfg = {0};
+       struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       struct hinic_hwif *hwif;
+       struct pci_dev *pdev;
+       u16 out_size;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       hwif = hwdev->hwif;
+       pdev = hwif->pdev;
+       rx_csum_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
+       rx_csum_cfg.rx_csum_offload = en;
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_CSUM,
+                                &rx_csum_cfg, sizeof(rx_csum_cfg),
+                                &rx_csum_cfg, &out_size);
+       if (err || !out_size || rx_csum_cfg.status) {
+               dev_err(&pdev->dev,
+                       "Failed to set rx csum offload, ret = %d\n",
+                       rx_csum_cfg.status);
+               return -EINVAL;
+       }
+
+       return 0;
+}
index f6e3220..02d896e 100644 (file)
@@ -183,6 +183,15 @@ struct hinic_tso_config {
        u8      resv2[3];
 };
 
+struct hinic_checksum_offload {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_id;
+       u16     rsvd1;
+       u32     rx_csum_offload;
+};
 int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
                       u16 vlan_id);
 
@@ -213,4 +222,5 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev,
 
 int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state);
 
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en);
 #endif
index 4c0f7ed..f86f2e6 100644 (file)
@@ -89,6 +89,28 @@ static void rxq_stats_init(struct hinic_rxq *rxq)
        hinic_rxq_clean_stats(rxq);
 }
 
+static void rx_csum(struct hinic_rxq *rxq, u16 cons_idx,
+                   struct sk_buff *skb)
+{
+       struct net_device *netdev = rxq->netdev;
+       struct hinic_rq_cqe *cqe;
+       struct hinic_rq *rq;
+       u32 csum_err;
+       u32 status;
+
+       rq = rxq->rq;
+       cqe = rq->cqe[cons_idx];
+       status = be32_to_cpu(cqe->status);
+       csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR);
+
+       if (!(netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       if (!csum_err)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       else
+               skb->ip_summed = CHECKSUM_NONE;
+}
 /**
  * rx_alloc_skb - allocate skb and map it to dma address
  * @rxq: rx queue
@@ -207,9 +229,9 @@ skb_out:
                wmb();  /* write all the wqes before update PI */
 
                hinic_rq_update(rxq->rq, prod_idx);
+               tasklet_schedule(&rxq->rx_task);
        }
 
-       tasklet_schedule(&rxq->rx_task);
        return i;
 }
 
@@ -328,6 +350,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
 
                rx_unmap_skb(rxq, hinic_sge_to_dma(&sge));
 
+               rx_csum(rxq, ci, skb);
+
                prefetch(skb->data);
 
                pkt_len = sge.len;
index 27c9af4..ab3faba 100644 (file)
 
 #include "hinic_hw_qp.h"
 
+#define HINIC_RX_CSUM_OFFLOAD_EN       0xFFF
+#define HINIC_RX_CSUM_HW_CHECK_NONE    BIT(7)
+#define HINIC_RX_CSUM_IPSU_OTHER_ERR   BIT(8)
+
 struct hinic_rxq_stats {
        u64                     pkts;
        u64                     bytes;
index 760b2ad..2092554 100644 (file)
@@ -2455,7 +2455,8 @@ static void emac_adjust_link(struct net_device *ndev)
        dev->phy.duplex = phy->duplex;
        dev->phy.pause = phy->pause;
        dev->phy.asym_pause = phy->asym_pause;
-       dev->phy.advertising = phy->advertising;
+       ethtool_convert_link_mode_to_legacy_u32(&dev->phy.advertising,
+                                               phy->advertising);
 }
 
 static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum)
@@ -2490,7 +2491,8 @@ static int emac_mdio_phy_start_aneg(struct mii_phy *phy,
        phy_dev->autoneg = phy->autoneg;
        phy_dev->speed = phy->speed;
        phy_dev->duplex = phy->duplex;
-       phy_dev->advertising = phy->advertising;
+       ethtool_convert_legacy_u32_to_link_mode(phy_dev->advertising,
+                                               phy->advertising);
        return phy_start_aneg(phy_dev);
 }
 
@@ -2624,7 +2626,8 @@ static int emac_dt_phy_connect(struct emac_instance *dev,
        dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask;
        dev->phy.def->name = dev->phy_dev->drv->name;
        dev->phy.def->ops = &emac_dt_mdio_phy_ops;
-       dev->phy.features = dev->phy_dev->supported;
+       ethtool_convert_link_mode_to_legacy_u32(&dev->phy.features,
+                                               dev->phy_dev->supported);
        dev->phy.address = dev->phy_dev->mdio.addr;
        dev->phy.mode = dev->phy_dev->interface;
        return 0;
index 59e1bc0..f05c91d 100644 (file)
@@ -33,7 +33,7 @@ config E100
          to identify the adapter.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e100.rst>.
+         <file:Documentation/networking/device_drivers/intel/e100.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e100.
@@ -49,7 +49,7 @@ config E1000
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e1000.rst>.
+         <file:Documentation/networking/device_drivers/intel/e1000.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e1000.
@@ -69,7 +69,7 @@ config E1000E
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/e1000e.rst>.
+         <file:Documentation/networking/device_drivers/intel/e1000e.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called e1000e.
@@ -97,7 +97,7 @@ config IGB
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/igb.rst>.
+         <file:Documentation/networking/device_drivers/intel/igb.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called igb.
@@ -133,7 +133,7 @@ config IGBVF
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/igbvf.rst>.
+         <file:Documentation/networking/device_drivers/intel/igbvf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called igbvf.
@@ -150,7 +150,7 @@ config IXGB
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgb.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgb.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgb.
@@ -168,7 +168,7 @@ config IXGBE
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgbe.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgbe.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgbe.
@@ -220,7 +220,7 @@ config IXGBEVF
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ixgbevf.rst>.
+         <file:Documentation/networking/device_drivers/intel/ixgbevf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ixgbevf.  MSI-X interrupt support is required
@@ -247,7 +247,7 @@ config I40E
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/i40e.rst>.
+         <file:Documentation/networking/device_drivers/intel/i40e.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called i40e.
@@ -282,7 +282,7 @@ config I40EVF
          This driver was formerly named i40evf.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/iavf.rst>.
+         <file:Documentation/networking/device_drivers/intel/iavf.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called iavf.  MSI-X interrupt support is required
@@ -300,7 +300,7 @@ config ICE
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/ice.rst>.
+         <file:Documentation/networking/device_drivers/intel/ice.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called ice.
@@ -318,7 +318,7 @@ config FM10K
          <http://support.intel.com>
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/fm10k.rst>.
+         <file:Documentation/networking/device_drivers/intel/fm10k.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called fm10k.  MSI-X interrupt support is required
index 7c4b554..5e5c57d 100644 (file)
@@ -2225,11 +2225,13 @@ static int e100_poll(struct napi_struct *napi, int budget)
        e100_rx_clean(nic, &work_done, budget);
        e100_tx_clean(nic);
 
-       /* If budget not fully consumed, exit the polling mode */
-       if (work_done < budget) {
-               napi_complete_done(napi, work_done);
+       /* If budget fully consumed, continue polling */
+       if (work_done == budget)
+               return budget;
+
+       /* only re-enable interrupt if stack agrees polling is really done */
+       if (likely(napi_complete_done(napi, work_done)))
                e100_enable_irq(nic);
-       }
 
        return work_done;
 }
index 43b6d3c..8fe9af0 100644 (file)
@@ -3803,14 +3803,15 @@ static int e1000_clean(struct napi_struct *napi, int budget)
 
        adapter->clean_rx(adapter, &adapter->rx_ring[0], &work_done, budget);
 
-       if (!tx_clean_complete)
-               work_done = budget;
+       if (!tx_clean_complete || work_done == budget)
+               return budget;
 
-       /* If budget not fully consumed, exit the polling mode */
-       if (work_done < budget) {
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (likely(adapter->itr_setting & 3))
                        e1000_set_itr(adapter);
-               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->flags))
                        e1000_irq_enable(adapter);
        }
index c760dc7..be13227 100644 (file)
@@ -505,6 +505,9 @@ extern const struct e1000_info e1000_es2_info;
 void e1000e_ptp_init(struct e1000_adapter *adapter);
 void e1000e_ptp_remove(struct e1000_adapter *adapter);
 
+u64 e1000e_read_systim(struct e1000_adapter *adapter,
+                      struct ptp_system_timestamp *sts);
+
 static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
 {
        return hw->phy.ops.reset(hw);
index 16a73bd..308c006 100644 (file)
@@ -2651,9 +2651,9 @@ err:
 /**
  * e1000e_poll - NAPI Rx polling callback
  * @napi: struct associated with this polling callback
- * @weight: number of packets driver is allowed to process this poll
+ * @budget: number of packets driver is allowed to process this poll
  **/
-static int e1000e_poll(struct napi_struct *napi, int weight)
+static int e1000e_poll(struct napi_struct *napi, int budget)
 {
        struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter,
                                                     napi);
@@ -2667,16 +2667,17 @@ static int e1000e_poll(struct napi_struct *napi, int weight)
            (adapter->rx_ring->ims_val & adapter->tx_ring->ims_val))
                tx_cleaned = e1000_clean_tx_irq(adapter->tx_ring);
 
-       adapter->clean_rx(adapter->rx_ring, &work_done, weight);
+       adapter->clean_rx(adapter->rx_ring, &work_done, budget);
 
-       if (!tx_cleaned)
-               work_done = weight;
+       if (!tx_cleaned || work_done == budget)
+               return budget;
 
-       /* If weight not fully consumed, exit the polling mode */
-       if (work_done < weight) {
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (adapter->itr_setting & 3)
                        e1000_set_itr(adapter);
-               napi_complete_done(napi, work_done);
                if (!test_bit(__E1000_DOWN, &adapter->state)) {
                        if (adapter->msix_entries)
                                ew32(IMS, adapter->rx_ring->ims_val);
@@ -4319,13 +4320,16 @@ void e1000e_reinit_locked(struct e1000_adapter *adapter)
 /**
  * e1000e_sanitize_systim - sanitize raw cycle counter reads
  * @hw: pointer to the HW structure
- * @systim: time value read, sanitized and returned
+ * @systim: PHC time value read, sanitized and returned
+ * @sts: structure to hold system time before and after reading SYSTIML,
+ * may be NULL
  *
  * Errata for 82574/82583 possible bad bits read from SYSTIMH/L:
  * check to see that the time is incrementing at a reasonable
  * rate and is a multiple of incvalue.
  **/
-static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim)
+static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim,
+                                 struct ptp_system_timestamp *sts)
 {
        u64 time_delta, rem, temp;
        u64 systim_next;
@@ -4335,7 +4339,9 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim)
        incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK;
        for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) {
                /* latch SYSTIMH on read of SYSTIML */
+               ptp_read_system_prets(sts);
                systim_next = (u64)er32(SYSTIML);
+               ptp_read_system_postts(sts);
                systim_next |= (u64)er32(SYSTIMH) << 32;
 
                time_delta = systim_next - systim;
@@ -4353,15 +4359,16 @@ static u64 e1000e_sanitize_systim(struct e1000_hw *hw, u64 systim)
 }
 
 /**
- * e1000e_cyclecounter_read - read raw cycle counter (used by time counter)
- * @cc: cyclecounter structure
+ * e1000e_read_systim - read SYSTIM register
+ * @adapter: board private structure
+ * @sts: structure which will contain system time before and after reading
+ * SYSTIML, may be NULL
  **/
-static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
+u64 e1000e_read_systim(struct e1000_adapter *adapter,
+                      struct ptp_system_timestamp *sts)
 {
-       struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
-                                                    cc);
        struct e1000_hw *hw = &adapter->hw;
-       u32 systimel, systimeh;
+       u32 systimel, systimel_2, systimeh;
        u64 systim;
        /* SYSTIMH latching upon SYSTIML read does not work well.
         * This means that if SYSTIML overflows after we read it but before
@@ -4369,11 +4376,15 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
         * will experience a huge non linear increment in the systime value
         * to fix that we test for overflow and if true, we re-read systime.
         */
+       ptp_read_system_prets(sts);
        systimel = er32(SYSTIML);
+       ptp_read_system_postts(sts);
        systimeh = er32(SYSTIMH);
        /* Is systimel is so large that overflow is possible? */
        if (systimel >= (u32)0xffffffff - E1000_TIMINCA_INCVALUE_MASK) {
-               u32 systimel_2 = er32(SYSTIML);
+               ptp_read_system_prets(sts);
+               systimel_2 = er32(SYSTIML);
+               ptp_read_system_postts(sts);
                if (systimel > systimel_2) {
                        /* There was an overflow, read again SYSTIMH, and use
                         * systimel_2
@@ -4386,12 +4397,24 @@ static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
        systim |= (u64)systimeh << 32;
 
        if (adapter->flags2 & FLAG2_CHECK_SYSTIM_OVERFLOW)
-               systim = e1000e_sanitize_systim(hw, systim);
+               systim = e1000e_sanitize_systim(hw, systim, sts);
 
        return systim;
 }
 
 /**
+ * e1000e_cyclecounter_read - read raw cycle counter (used by time counter)
+ * @cc: cyclecounter structure
+ **/
+static u64 e1000e_cyclecounter_read(const struct cyclecounter *cc)
+{
+       struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
+                                                    cc);
+
+       return e1000e_read_systim(adapter, NULL);
+}
+
+/**
  * e1000_sw_init - Initialize general software structures (struct e1000_adapter)
  * @adapter: board private structure to initialize
  *
index 37c7694..1a4c65d 100644 (file)
@@ -161,22 +161,30 @@ static int e1000e_phc_getcrosststamp(struct ptp_clock_info *ptp,
 #endif/*CONFIG_E1000E_HWTS*/
 
 /**
- * e1000e_phc_gettime - Reads the current time from the hardware clock
+ * e1000e_phc_gettimex - Reads the current time from the hardware clock and
+ *                       system clock
  * @ptp: ptp clock structure
- * @ts: timespec structure to hold the current time value
+ * @ts: timespec structure to hold the current PHC time
+ * @sts: structure to hold the current system time
  *
  * Read the timecounter and return the correct value in ns after converting
  * it into a struct timespec.
  **/
-static int e1000e_phc_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int e1000e_phc_gettimex(struct ptp_clock_info *ptp,
+                              struct timespec64 *ts,
+                              struct ptp_system_timestamp *sts)
 {
        struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter,
                                                     ptp_clock_info);
        unsigned long flags;
-       u64 ns;
+       u64 cycles, ns;
 
        spin_lock_irqsave(&adapter->systim_lock, flags);
-       ns = timecounter_read(&adapter->tc);
+
+       /* NOTE: Non-monotonic SYSTIM readings may be returned */
+       cycles = e1000e_read_systim(adapter, sts);
+       ns = timecounter_cyc2time(&adapter->tc, cycles);
+
        spin_unlock_irqrestore(&adapter->systim_lock, flags);
 
        *ts = ns_to_timespec64(ns);
@@ -232,9 +240,12 @@ static void e1000e_systim_overflow_work(struct work_struct *work)
                                                     systim_overflow_work.work);
        struct e1000_hw *hw = &adapter->hw;
        struct timespec64 ts;
+       u64 ns;
 
-       adapter->ptp_clock_info.gettime64(&adapter->ptp_clock_info, &ts);
+       /* Update the timecounter */
+       ns = timecounter_read(&adapter->tc);
 
+       ts = ns_to_timespec64(ns);
        e_dbg("SYSTIM overflow check at %lld.%09lu\n",
              (long long) ts.tv_sec, ts.tv_nsec);
 
@@ -251,7 +262,7 @@ static const struct ptp_clock_info e1000e_ptp_clock_info = {
        .pps            = 0,
        .adjfreq        = e1000e_phc_adjfreq,
        .adjtime        = e1000e_phc_adjtime,
-       .gettime64      = e1000e_phc_gettime,
+       .gettimex64     = e1000e_phc_gettimex,
        .settime64      = e1000e_phc_settime,
        .enable         = e1000e_phc_enable,
 };
index 5b2a50e..6fd15a7 100644 (file)
@@ -1465,11 +1465,11 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* all work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-
-       /* re-enable the q_vector */
-       fm10k_qv_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               fm10k_qv_enable(q_vector);
 
        return min(work_done, budget - 1);
 }
index 876cac3..8de9085 100644 (file)
@@ -122,6 +122,7 @@ enum i40e_state_t {
        __I40E_MDD_EVENT_PENDING,
        __I40E_VFLR_EVENT_PENDING,
        __I40E_RESET_RECOVERY_PENDING,
+       __I40E_TIMEOUT_RECOVERY_PENDING,
        __I40E_MISC_IRQ_REQUESTED,
        __I40E_RESET_INTR_RECEIVED,
        __I40E_REINIT_REQUESTED,
@@ -146,6 +147,7 @@ enum i40e_state_t {
        __I40E_CLIENT_SERVICE_REQUESTED,
        __I40E_CLIENT_L2_CHANGE,
        __I40E_CLIENT_RESET,
+       __I40E_VIRTCHNL_OP_PENDING,
        /* This must be last as it determines the size of the BITMAP */
        __I40E_STATE_SIZE__,
 };
@@ -494,7 +496,6 @@ struct i40e_pf {
 #define I40E_HW_STOP_FW_LLDP                   BIT(16)
 #define I40E_HW_PORT_ID_VALID                  BIT(17)
 #define I40E_HW_RESTART_AUTONEG                        BIT(18)
-#define I40E_HW_STOPPABLE_FW_LLDP              BIT(19)
 
        u32 flags;
 #define I40E_FLAG_RX_CSUM_ENABLED              BIT(0)
index 501ee71..7ab61f6 100644 (file)
@@ -588,6 +588,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
            hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
            hw->aq.api_min_ver >= I40E_MINOR_VER_GET_LINK_INFO_XL710) {
                hw->flags |= I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE;
+               hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE;
+       }
+       if (hw->mac.type == I40E_MAC_X722 &&
+           hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+           hw->aq.api_min_ver >= I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722) {
+               hw->flags |= I40E_HW_FLAG_FW_LLDP_STOPPABLE;
        }
 
        /* Newer versions of firmware require lock when reading the NVM */
index 80e3eec..1150610 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR      0x0001
-#define I40E_FW_API_VERSION_MINOR_X722 0x0005
+#define I40E_FW_API_VERSION_MINOR_X722 0x0006
 #define I40E_FW_API_VERSION_MINOR_X710 0x0007
 
 #define I40E_FW_MINOR_VERSION(_h) ((_h)->mac.type == I40E_MAC_XL710 ? \
@@ -20,6 +20,8 @@
 
 /* API version 1.7 implements additional link and PHY-specific APIs  */
 #define I40E_MINOR_VER_GET_LINK_INFO_XL710 0x0007
+/* API version 1.6 for X722 devices adds ability to stop FW LLDP agent */
+#define I40E_MINOR_VER_FW_LLDP_STOPPABLE_X722 0x0006
 
 struct i40e_aq_desc {
        __le16 flags;
index 85f75b5..97a9b1f 100644 (file)
@@ -3723,6 +3723,9 @@ i40e_aq_set_dcb_parameters(struct i40e_hw *hw, bool dcb_enable,
                (struct i40e_aqc_set_dcb_parameters *)&desc.params.raw;
        i40e_status status;
 
+       if (!(hw->flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+               return I40E_ERR_DEVICE_NOT_SUPPORTED;
+
        i40e_fill_default_direct_cmd_desc(&desc,
                                          i40e_aqc_opc_set_dcb_parameters);
 
index 9f8464f..a6bc784 100644 (file)
@@ -906,6 +906,7 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw,
                ks->base.speed = SPEED_100;
                break;
        default:
+               ks->base.speed = SPEED_UNKNOWN;
                break;
        }
        ks->base.duplex = DUPLEX_FULL;
@@ -1335,6 +1336,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
        i40e_status status;
        u8 aq_failures;
        int err = 0;
+       u32 is_an;
 
        /* Changing the port's flow control is not supported if this isn't the
         * port's controlling PF
@@ -1347,15 +1349,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
        if (vsi != pf->vsi[pf->lan_vsi])
                return -EOPNOTSUPP;
 
-       if (pause->autoneg != ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
-           AUTONEG_ENABLE : AUTONEG_DISABLE)) {
+       is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED;
+       if (pause->autoneg != is_an) {
                netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
                return -EOPNOTSUPP;
        }
 
        /* If we have link and don't have autoneg */
-       if (!test_bit(__I40E_DOWN, pf->state) &&
-           !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) {
+       if (!test_bit(__I40E_DOWN, pf->state) && !is_an) {
                /* Send message that it might not necessarily work*/
                netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
        }
@@ -1406,7 +1407,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
                err = -EAGAIN;
        }
 
-       if (!test_bit(__I40E_DOWN, pf->state)) {
+       if (!test_bit(__I40E_DOWN, pf->state) && is_an) {
                /* Give it a little more time to try to come back */
                msleep(75);
                if (!test_bit(__I40E_DOWN, pf->state))
@@ -2377,7 +2378,8 @@ static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
                return -EOPNOTSUPP;
 
        /* only magic packet is supported */
-       if (wol->wolopts && (wol->wolopts != WAKE_MAGIC))
+       if (wol->wolopts && (wol->wolopts != WAKE_MAGIC)
+                         | (wol->wolopts != WAKE_FILTER))
                return -EOPNOTSUPP;
 
        /* is this a new value? */
@@ -4659,14 +4661,15 @@ flags_complete:
                return -EOPNOTSUPP;
 
        /* If the driver detected FW LLDP was disabled on init, this flag could
-        * be set, however we do not support _changing_ the flag if NPAR is
-        * enabled or FW API version < 1.7.  There are situations where older
-        * FW versions/NPAR enabled PFs could disable LLDP, however we _must_
-        * not allow the user to enable/disable LLDP with this flag on
-        * unsupported FW versions.
+        * be set, however we do not support _changing_ the flag:
+        * - on XL710 if NPAR is enabled or FW API version < 1.7
+        * - on X722 with FW API version < 1.6
+        * There are situations where older FW versions/NPAR enabled PFs could
+        * disable LLDP, however we _must_ not allow the user to enable/disable
+        * LLDP with this flag on unsupported FW versions.
         */
        if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
-               if (!(pf->hw_features & I40E_HW_STOPPABLE_FW_LLDP)) {
+               if (!(pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) {
                        dev_warn(&pf->pdev->dev,
                                 "Device does not support changing FW LLDP\n");
                        return -EOPNOTSUPP;
index a3f4533..6d5b13f 100644 (file)
@@ -26,8 +26,8 @@ static const char i40e_driver_string[] =
 #define DRV_KERN "-k"
 
 #define DRV_VERSION_MAJOR 2
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 2
+#define DRV_VERSION_MINOR 7
+#define DRV_VERSION_BUILD 6
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -338,6 +338,10 @@ static void i40e_tx_timeout(struct net_device *netdev)
                      (pf->tx_timeout_last_recovery + netdev->watchdog_timeo)))
                return;   /* don't do any new action before the next timeout */
 
+       /* don't kick off another recovery if one is already pending */
+       if (test_and_set_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state))
+               return;
+
        if (tx_ring) {
                head = i40e_get_head(tx_ring);
                /* Read interrupt register */
@@ -1493,8 +1497,7 @@ int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr)
        bool found = false;
        int bkt;
 
-       WARN(!spin_is_locked(&vsi->mac_filter_hash_lock),
-            "Missing mac_filter_hash_lock\n");
+       lockdep_assert_held(&vsi->mac_filter_hash_lock);
        hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
                if (ether_addr_equal(macaddr, f->macaddr)) {
                        __i40e_del_filter(vsi, f);
@@ -9632,6 +9635,7 @@ end_core_reset:
        clear_bit(__I40E_RESET_FAILED, pf->state);
 clear_recovery:
        clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
+       clear_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state);
 }
 
 /**
@@ -11332,16 +11336,15 @@ static int i40e_sw_init(struct i40e_pf *pf)
                /* IWARP needs one extra vector for CQP just like MISC.*/
                pf->num_iwarp_msix = (int)num_online_cpus() + 1;
        }
-       /* Stopping the FW LLDP engine is only supported on the
-        * XL710 with a FW ver >= 1.7.  Also, stopping FW LLDP
-        * engine is not supported if NPAR is functioning on this
-        * part
+       /* Stopping FW LLDP engine is supported on XL710 and X722
+        * starting from FW versions determined in i40e_init_adminq.
+        * Stopping the FW LLDP engine is not supported on XL710
+        * if NPAR is functioning so unset this hw flag in this case.
         */
        if (pf->hw.mac.type == I40E_MAC_XL710 &&
-           !pf->hw.func_caps.npar_enable &&
-           (pf->hw.aq.api_maj_ver > 1 ||
-            (pf->hw.aq.api_maj_ver == 1 && pf->hw.aq.api_min_ver > 6)))
-               pf->hw_features |= I40E_HW_STOPPABLE_FW_LLDP;
+           pf->hw.func_caps.npar_enable &&
+           (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+               pf->hw.flags &= ~I40E_HW_FLAG_FW_LLDP_STOPPABLE;
 
 #ifdef CONFIG_PCI_IOV
        if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
@@ -14302,23 +14305,23 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
                switch (hw->bus.speed) {
                case i40e_bus_speed_8000:
-                       strncpy(speed, "8.0", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "8.0", PCI_SPEED_SIZE); break;
                case i40e_bus_speed_5000:
-                       strncpy(speed, "5.0", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "5.0", PCI_SPEED_SIZE); break;
                case i40e_bus_speed_2500:
-                       strncpy(speed, "2.5", PCI_SPEED_SIZE); break;
+                       strlcpy(speed, "2.5", PCI_SPEED_SIZE); break;
                default:
                        break;
                }
                switch (hw->bus.width) {
                case i40e_bus_width_pcie_x8:
-                       strncpy(width, "8", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "8", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x4:
-                       strncpy(width, "4", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "4", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x2:
-                       strncpy(width, "2", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "2", PCI_WIDTH_SIZE); break;
                case i40e_bus_width_pcie_x1:
-                       strncpy(width, "1", PCI_WIDTH_SIZE); break;
+                       strlcpy(width, "1", PCI_WIDTH_SIZE); break;
                default:
                        break;
                }
index 1199f05..e6fc0af 100644 (file)
@@ -694,7 +694,7 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf)
        if (!IS_ERR_OR_NULL(pf->ptp_clock))
                return 0;
 
-       strncpy(pf->ptp_caps.name, i40e_driver_name,
+       strlcpy(pf->ptp_caps.name, i40e_driver_name,
                sizeof(pf->ptp_caps.name) - 1);
        pf->ptp_caps.owner = THIS_MODULE;
        pf->ptp_caps.max_adj = 999999999;
index aef3c89..a0b1575 100644 (file)
@@ -2667,10 +2667,11 @@ tx_only:
        if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR)
                q_vector->arm_wb_state = false;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-
-       i40e_update_enable_itr(vsi, q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               i40e_update_enable_itr(vsi, q_vector);
 
        return min(work_done, budget - 1);
 }
@@ -3473,6 +3474,8 @@ static inline int i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
        tx_desc->cmd_type_offset_bsz =
                        build_ctob(td_cmd, td_offset, size, td_tag);
 
+       skb_tx_timestamp(skb);
+
        /* Force memory writes to complete before letting h/w know there
         * are new descriptors to fetch.
         *
@@ -3526,6 +3529,7 @@ static int i40e_xmit_xdp_ring(struct xdp_frame *xdpf,
        u16 i = xdp_ring->next_to_use;
        struct i40e_tx_buffer *tx_bi;
        struct i40e_tx_desc *tx_desc;
+       void *data = xdpf->data;
        u32 size = xdpf->len;
        dma_addr_t dma;
 
@@ -3533,8 +3537,7 @@ static int i40e_xmit_xdp_ring(struct xdp_frame *xdpf,
                xdp_ring->tx_stats.tx_busy++;
                return I40E_XDP_CONSUMED;
        }
-
-       dma = dma_map_single(xdp_ring->dev, xdpf->data, size, DMA_TO_DEVICE);
+       dma = dma_map_single(xdp_ring->dev, data, size, DMA_TO_DEVICE);
        if (dma_mapping_error(xdp_ring->dev, dma))
                return I40E_XDP_CONSUMED;
 
@@ -3652,8 +3655,6 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        if (tsyn)
                tx_flags |= I40E_TX_FLAGS_TSYN;
 
-       skb_tx_timestamp(skb);
-
        /* always enable CRC insertion offload */
        td_cmd |= I40E_TX_DESC_CMD_ICRC;
 
index 7df969c..2781ab9 100644 (file)
@@ -615,6 +615,7 @@ struct i40e_hw {
 #define I40E_HW_FLAG_802_1AD_CAPABLE        BIT_ULL(1)
 #define I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE  BIT_ULL(2)
 #define I40E_HW_FLAG_NVM_READ_REQUIRES_LOCK BIT_ULL(3)
+#define I40E_HW_FLAG_FW_LLDP_STOPPABLE      BIT_ULL(4)
        u64 flags;
 
        /* Used in set switch config AQ command */
index ac5698e..2ac23eb 100644 (file)
@@ -1112,7 +1112,8 @@ static i40e_status i40e_config_vf_promiscuous_mode(struct i40e_vf *vf,
        if (!i40e_vc_isvalid_vsi_id(vf, vsi_id) || !vsi)
                return I40E_ERR_PARAM;
 
-       if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {
+       if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
+           (allmulti || alluni)) {
                dev_err(&pf->pdev->dev,
                        "Unprivileged VF %d is attempting to configure promiscuous mode\n",
                        vf->vf_id);
@@ -1675,13 +1676,20 @@ err_out:
 int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
+       int ret = 0;
+
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
 
        if (num_vfs) {
                if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
                        pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
                        i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
                }
-               return i40e_pci_sriov_enable(pdev, num_vfs);
+               ret = i40e_pci_sriov_enable(pdev, num_vfs);
+               goto sriov_configure_out;
        }
 
        if (!pci_vfs_assigned(pf->pdev)) {
@@ -1690,9 +1698,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
                i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
        } else {
                dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto sriov_configure_out;
        }
-       return 0;
+sriov_configure_out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
+       return ret;
 }
 
 /***********************virtual channel routines******************/
@@ -3893,6 +3904,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                goto error_param;
        }
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        if (is_multicast_ether_addr(mac)) {
                dev_err(&pf->pdev->dev,
                        "Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
@@ -3941,6 +3957,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
        dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n");
 
 error_param:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -3992,6 +4009,11 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4107,6 +4129,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
        ret = 0;
 
 error_pvid:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4128,6 +4151,11 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4154,6 +4182,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
 
        vf->tx_rate = max_tx_rate;
 error:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4174,6 +4203,11 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        ret = i40e_validate_vf(pf, vf_id);
        if (ret)
@@ -4209,6 +4243,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
        ret = 0;
 
 error_param:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4230,6 +4265,11 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
        int abs_vf_id;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4273,6 +4313,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
                               0, (u8 *)&pfe, sizeof(pfe), NULL);
 
 error_out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4294,6 +4335,11 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4327,6 +4373,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
                ret = -EIO;
        }
 out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
 
@@ -4345,15 +4392,22 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
        struct i40e_vf *vf;
        int ret = 0;
 
+       if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+               dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+               return -EAGAIN;
+       }
+
        /* validate the request */
        if (vf_id >= pf->num_alloc_vfs) {
                dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (pf->flags & I40E_FLAG_MFP_ENABLED) {
                dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        vf = &pf->vf[vf_id];
@@ -4376,5 +4430,6 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
        }
 
 out:
+       clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
        return ret;
 }
index bf67d62..f962102 100644 (file)
@@ -13,9 +13,9 @@
 #define I40E_DEFAULT_NUM_MDD_EVENTS_ALLOWED    3
 #define I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED  10
 
-#define I40E_VLAN_PRIORITY_SHIFT       12
+#define I40E_VLAN_PRIORITY_SHIFT       13
 #define I40E_VLAN_MASK                 0xFFF
-#define I40E_PRIORITY_MASK             0x7000
+#define I40E_PRIORITY_MASK             0xE000
 
 /* Various queue ctrls */
 enum i40e_queue_ctrl {
index fb9bfad..9b4d7ce 100644 (file)
@@ -1761,10 +1761,11 @@ tx_only:
        if (vsi->back->flags & IAVF_TXR_FLAGS_WB_ON_ITR)
                q_vector->arm_wb_state = false;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-
-       iavf_update_enable_itr(vsi, q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               iavf_update_enable_itr(vsi, q_vector);
 
        return min(work_done, budget - 1);
 }
@@ -2343,6 +2344,8 @@ static inline void iavf_tx_map(struct iavf_ring *tx_ring, struct sk_buff *skb,
        tx_desc->cmd_type_offset_bsz =
                        build_ctob(td_cmd, td_offset, size, td_tag);
 
+       skb_tx_timestamp(skb);
+
        /* Force memory writes to complete before letting h/w know there
         * are new descriptors to fetch.
         *
@@ -2461,8 +2464,6 @@ static netdev_tx_t iavf_xmit_frame_ring(struct sk_buff *skb,
        if (tso < 0)
                goto out_drop;
 
-       skb_tx_timestamp(skb);
-
        /* always enable CRC insertion offload */
        td_cmd |= IAVF_TX_DESC_CMD_ICRC;
 
index b854837..a385575 100644 (file)
@@ -52,7 +52,6 @@ extern const char ice_drv_ver[];
 #define ICE_MBXQ_LEN           64
 #define ICE_MIN_MSIX           2
 #define ICE_NO_VSI             0xffff
-#define ICE_MAX_VSI_ALLOC      130
 #define ICE_MAX_TXQS           2048
 #define ICE_MAX_RXQS           2048
 #define ICE_VSI_MAP_CONTIG     0
@@ -97,14 +96,14 @@ extern const char ice_drv_ver[];
 #define ice_for_each_vsi(pf, i) \
        for ((i) = 0; (i) < (pf)->num_alloc_vsi; (i)++)
 
-/* Macros for each tx/rx ring in a VSI */
+/* Macros for each Tx/Rx ring in a VSI */
 #define ice_for_each_txq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_txq; (i)++)
 
 #define ice_for_each_rxq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_rxq; (i)++)
 
-/* Macros for each allocated tx/rx ring whether used or not in a VSI */
+/* Macros for each allocated Tx/Rx ring whether used or not in a VSI */
 #define ice_for_each_alloc_txq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->alloc_txq; (i)++)
 
@@ -113,7 +112,9 @@ extern const char ice_drv_ver[];
 
 struct ice_tc_info {
        u16 qoffset;
-       u16 qcount;
+       u16 qcount_tx;
+       u16 qcount_rx;
+       u8 netdev_tc;
 };
 
 struct ice_tc_cfg {
@@ -149,10 +150,10 @@ enum ice_state {
        __ICE_RESET_FAILED,             /* set by reset/rebuild */
        /* When checking for the PF to be in a nominal operating state, the
         * bits that are grouped at the beginning of the list need to be
-        * checked.  Bits occurring before __ICE_STATE_NOMINAL_CHECK_BITS will
-        * be checked.  If you need to add a bit into consideration for nominal
+        * checked. Bits occurring before __ICE_STATE_NOMINAL_CHECK_BITS will
+        * be checked. If you need to add a bit into consideration for nominal
         * operating state, it must be added before
-        * __ICE_STATE_NOMINAL_CHECK_BITS.  Do not move this entry's position
+        * __ICE_STATE_NOMINAL_CHECK_BITS. Do not move this entry's position
         * without appropriate consideration.
         */
        __ICE_STATE_NOMINAL_CHECK_BITS,
@@ -182,8 +183,8 @@ struct ice_vsi {
        struct ice_sw *vsw;              /* switch this VSI is on */
        struct ice_pf *back;             /* back pointer to PF */
        struct ice_port_info *port_info; /* back pointer to port_info */
-       struct ice_ring **rx_rings;      /* rx ring array */
-       struct ice_ring **tx_rings;      /* tx ring array */
+       struct ice_ring **rx_rings;      /* Rx ring array */
+       struct ice_ring **tx_rings;      /* Tx ring array */
        struct ice_q_vector **q_vectors; /* q_vector array */
 
        irqreturn_t (*irq_handler)(int irq, void *data);
@@ -200,8 +201,8 @@ struct ice_vsi {
        int sw_base_vector;             /* Irq base for OS reserved vectors */
        int hw_base_vector;             /* HW (absolute) index of a vector */
        enum ice_vsi_type type;
-       u16 vsi_num;                     /* HW (absolute) index of this VSI */
-       u16 idx;                         /* software index in pf->vsi[] */
+       u16 vsi_num;                    /* HW (absolute) index of this VSI */
+       u16 idx;                        /* software index in pf->vsi[] */
 
        /* Interrupt thresholds */
        u16 work_lmt;
@@ -254,8 +255,8 @@ struct ice_q_vector {
        struct ice_ring_container tx;
        struct irq_affinity_notify affinity_notify;
        u16 v_idx;                      /* index in the vsi->q_vector array. */
-       u8 num_ring_tx;                 /* total number of tx rings in vector */
-       u8 num_ring_rx;                 /* total number of rx rings in vector */
+       u8 num_ring_tx;                 /* total number of Tx rings in vector */
+       u8 num_ring_rx;                 /* total number of Rx rings in vector */
        char name[ICE_INT_NAME_STR_LEN];
        /* in usecs, need to use ice_intrl_to_usecs_reg() before writing this
         * value to the device
@@ -307,10 +308,10 @@ struct ice_pf {
        u32 hw_oicr_idx;        /* Other interrupt cause vector HW index */
        u32 num_avail_hw_msix;  /* remaining HW MSIX vectors left unclaimed */
        u32 num_lan_msix;       /* Total MSIX vectors for base driver */
-       u16 num_lan_tx;         /* num lan tx queues setup */
-       u16 num_lan_rx;         /* num lan rx queues setup */
-       u16 q_left_tx;          /* remaining num tx queues left unclaimed */
-       u16 q_left_rx;          /* remaining num rx queues left unclaimed */
+       u16 num_lan_tx;         /* num lan Tx queues setup */
+       u16 num_lan_rx;         /* num lan Rx queues setup */
+       u16 q_left_tx;          /* remaining num Tx queues left unclaimed */
+       u16 q_left_rx;          /* remaining num Rx queues left unclaimed */
        u16 next_vsi;           /* Next free slot in pf->vsi[] - 0-based! */
        u16 num_alloc_vsi;
        u16 corer_count;        /* Core reset count */
index 6653555..fcdcd80 100644 (file)
@@ -5,7 +5,7 @@
 #define _ICE_ADMINQ_CMD_H_
 
 /* This header file defines the Admin Queue commands, error codes and
- * descriptor format.  It is shared between Firmware and Software.
+ * descriptor format. It is shared between Firmware and Software.
  */
 
 #define ICE_MAX_VSI                    768
@@ -87,6 +87,7 @@ struct ice_aqc_list_caps {
 /* Device/Function buffer entry, repeated per reported capability */
 struct ice_aqc_list_caps_elem {
        __le16 cap;
+#define ICE_AQC_CAPS_VALID_FUNCTIONS                   0x0005
 #define ICE_AQC_CAPS_SRIOV                             0x0012
 #define ICE_AQC_CAPS_VF                                        0x0013
 #define ICE_AQC_CAPS_VSI                               0x0017
@@ -462,7 +463,7 @@ struct ice_aqc_sw_rules {
 };
 
 /* Add/Update/Get/Remove lookup Rx/Tx command/response entry
- * This structures describes the lookup rules and associated actions.  "index"
+ * This structures describes the lookup rules and associated actions. "index"
  * is returned as part of a response to a successful Add command, and can be
  * used to identify the rule for Update/Get/Remove commands.
  */
@@ -1065,10 +1066,10 @@ struct ice_aqc_nvm {
 #define ICE_AQC_NVM_LAST_CMD           BIT(0)
 #define ICE_AQC_NVM_PCIR_REQ           BIT(0)  /* Used by NVM Update reply */
 #define ICE_AQC_NVM_PRESERVATION_S     1
-#define ICE_AQC_NVM_PRESERVATION_M     (3 << CSR_AQ_NVM_PRESERVATION_S)
-#define ICE_AQC_NVM_NO_PRESERVATION    (0 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVATION_M     (3 << ICE_AQC_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_NO_PRESERVATION    (0 << ICE_AQC_NVM_PRESERVATION_S)
 #define ICE_AQC_NVM_PRESERVE_ALL       BIT(1)
-#define ICE_AQC_NVM_PRESERVE_SELECTED  (3 << CSR_AQ_NVM_PRESERVATION_S)
+#define ICE_AQC_NVM_PRESERVE_SELECTED  (3 << ICE_AQC_NVM_PRESERVATION_S)
 #define ICE_AQC_NVM_FLASH_ONLY         BIT(7)
        __le16 module_typeid;
        __le16 length;
@@ -1110,7 +1111,7 @@ struct ice_aqc_get_set_rss_keys {
 };
 
 /* Get/Set RSS LUT (indirect 0x0B05/0x0B03) */
-struct  ice_aqc_get_set_rss_lut {
+struct ice_aqc_get_set_rss_lut {
 #define ICE_AQC_GSET_RSS_LUT_VSI_VALID BIT(15)
 #define ICE_AQC_GSET_RSS_LUT_VSI_ID_S  0
 #define ICE_AQC_GSET_RSS_LUT_VSI_ID_M  (0x1FF << ICE_AQC_GSET_RSS_LUT_VSI_ID_S)
@@ -1314,10 +1315,10 @@ struct ice_aqc_get_clear_fw_log {
  * @params: command-specific parameters
  *
  * Descriptor format for commands the driver posts on the Admin Transmit Queue
- * (ATQ).  The firmware writes back onto the command descriptor and returns
- * the result of the command.  Asynchronous events that are not an immediate
+ * (ATQ). The firmware writes back onto the command descriptor and returns
+ * the result of the command. Asynchronous events that are not an immediate
  * result of the command are written to the Admin Receive Queue (ARQ) using
- * the same descriptor format.  Descriptors are in little-endian notation with
+ * the same descriptor format. Descriptors are in little-endian notation with
  * 32-bit words.
  */
 struct ice_aq_desc {
@@ -1379,10 +1380,10 @@ struct ice_aq_desc {
 
 /* error codes */
 enum ice_aq_err {
-       ICE_AQ_RC_OK            = 0,  /* success */
+       ICE_AQ_RC_OK            = 0,  /* Success */
        ICE_AQ_RC_ENOMEM        = 9,  /* Out of memory */
        ICE_AQ_RC_EBUSY         = 12, /* Device or resource busy */
-       ICE_AQ_RC_EEXIST        = 13, /* object already exists */
+       ICE_AQ_RC_EEXIST        = 13, /* Object already exists */
        ICE_AQ_RC_ENOSPC        = 16, /* No space left or allocation failure */
 };
 
index 554fd70..4c1d35d 100644 (file)
@@ -405,9 +405,7 @@ static enum ice_status ice_init_fltr_mgmt_struct(struct ice_hw *hw)
 
        INIT_LIST_HEAD(&sw->vsi_list_map_head);
 
-       ice_init_def_sw_recp(hw);
-
-       return 0;
+       return ice_init_def_sw_recp(hw);
 }
 
 /**
@@ -715,7 +713,7 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
 
        hw->evb_veb = true;
 
-       /* Query the allocated resources for tx scheduler */
+       /* Query the allocated resources for Tx scheduler */
        status = ice_sched_query_res_alloc(hw);
        if (status) {
                ice_debug(hw, ICE_DBG_SCHED,
@@ -958,7 +956,7 @@ enum ice_status ice_reset(struct ice_hw *hw, enum ice_reset_req req)
  * ice_copy_rxq_ctx_to_hw
  * @hw: pointer to the hardware structure
  * @ice_rxq_ctx: pointer to the rxq context
- * @rxq_index: the index of the rx queue
+ * @rxq_index: the index of the Rx queue
  *
  * Copies rxq context from dense structure to hw register space
  */
@@ -1014,7 +1012,7 @@ static const struct ice_ctx_ele ice_rlan_ctx_info[] = {
  * ice_write_rxq_ctx
  * @hw: pointer to the hardware structure
  * @rlan_ctx: pointer to the rxq context
- * @rxq_index: the index of the rx queue
+ * @rxq_index: the index of the Rx queue
  *
  * Converts rxq context from sparse to dense structure and then writes
  * it to hw register space
@@ -1387,6 +1385,27 @@ void ice_release_res(struct ice_hw *hw, enum ice_aq_res_ids res)
 }
 
 /**
+ * ice_get_guar_num_vsi - determine number of guar VSI for a PF
+ * @hw: pointer to the hw structure
+ *
+ * Determine the number of valid functions by going through the bitmap returned
+ * from parsing capabilities and use this to calculate the number of VSI per PF.
+ */
+static u32 ice_get_guar_num_vsi(struct ice_hw *hw)
+{
+       u8 funcs;
+
+#define ICE_CAPS_VALID_FUNCS_M 0xFF
+       funcs = hweight8(hw->dev_caps.common_cap.valid_functions &
+                        ICE_CAPS_VALID_FUNCS_M);
+
+       if (!funcs)
+               return 0;
+
+       return ICE_MAX_VSI / funcs;
+}
+
+/**
  * ice_parse_caps - parse function/device capabilities
  * @hw: pointer to the hw struct
  * @buf: pointer to a buffer containing function/device capability records
@@ -1428,6 +1447,12 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                u16 cap = le16_to_cpu(cap_resp->cap);
 
                switch (cap) {
+               case ICE_AQC_CAPS_VALID_FUNCTIONS:
+                       caps->valid_functions = number;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Valid Functions = %d\n",
+                                 caps->valid_functions);
+                       break;
                case ICE_AQC_CAPS_SRIOV:
                        caps->sr_iov_1_1 = (number == 1);
                        ice_debug(hw, ICE_DBG_INIT,
@@ -1457,10 +1482,10 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                                          "HW caps: Dev.VSI cnt = %d\n",
                                          dev_p->num_vsi_allocd_to_host);
                        } else if (func_p) {
-                               func_p->guaranteed_num_vsi = number;
+                               func_p->guar_num_vsi = ice_get_guar_num_vsi(hw);
                                ice_debug(hw, ICE_DBG_INIT,
                                          "HW caps: Func.VSI cnt = %d\n",
-                                         func_p->guaranteed_num_vsi);
+                                         number);
                        }
                        break;
                case ICE_AQC_CAPS_RSS:
@@ -1688,8 +1713,7 @@ void ice_clear_pxe_mode(struct ice_hw *hw)
  * If no bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned
  * If more than one bit gets set, ICE_LINK_SPEED_UNKNOWN will be returned
  */
-static u16
-ice_get_link_speed_based_on_phy_type(u64 phy_type_low)
+static u16 ice_get_link_speed_based_on_phy_type(u64 phy_type_low)
 {
        u16 speed_phy_type_low = ICE_AQ_LINK_SPEED_UNKNOWN;
 
index 84c9672..2bf5e11 100644 (file)
@@ -3,6 +3,26 @@
 
 #include "ice_common.h"
 
+#define ICE_CQ_INIT_REGS(qinfo, prefix)                                \
+do {                                                           \
+       (qinfo)->sq.head = prefix##_ATQH;                       \
+       (qinfo)->sq.tail = prefix##_ATQT;                       \
+       (qinfo)->sq.len = prefix##_ATQLEN;                      \
+       (qinfo)->sq.bah = prefix##_ATQBAH;                      \
+       (qinfo)->sq.bal = prefix##_ATQBAL;                      \
+       (qinfo)->sq.len_mask = prefix##_ATQLEN_ATQLEN_M;        \
+       (qinfo)->sq.len_ena_mask = prefix##_ATQLEN_ATQENABLE_M; \
+       (qinfo)->sq.head_mask = prefix##_ATQH_ATQH_M;           \
+       (qinfo)->rq.head = prefix##_ARQH;                       \
+       (qinfo)->rq.tail = prefix##_ARQT;                       \
+       (qinfo)->rq.len = prefix##_ARQLEN;                      \
+       (qinfo)->rq.bah = prefix##_ARQBAH;                      \
+       (qinfo)->rq.bal = prefix##_ARQBAL;                      \
+       (qinfo)->rq.len_mask = prefix##_ARQLEN_ARQLEN_M;        \
+       (qinfo)->rq.len_ena_mask = prefix##_ARQLEN_ARQENABLE_M; \
+       (qinfo)->rq.head_mask = prefix##_ARQH_ARQH_M;           \
+} while (0)
+
 /**
  * ice_adminq_init_regs - Initialize AdminQ registers
  * @hw: pointer to the hardware structure
@@ -13,23 +33,7 @@ static void ice_adminq_init_regs(struct ice_hw *hw)
 {
        struct ice_ctl_q_info *cq = &hw->adminq;
 
-       cq->sq.head = PF_FW_ATQH;
-       cq->sq.tail = PF_FW_ATQT;
-       cq->sq.len = PF_FW_ATQLEN;
-       cq->sq.bah = PF_FW_ATQBAH;
-       cq->sq.bal = PF_FW_ATQBAL;
-       cq->sq.len_mask = PF_FW_ATQLEN_ATQLEN_M;
-       cq->sq.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
-       cq->sq.head_mask = PF_FW_ATQH_ATQH_M;
-
-       cq->rq.head = PF_FW_ARQH;
-       cq->rq.tail = PF_FW_ARQT;
-       cq->rq.len = PF_FW_ARQLEN;
-       cq->rq.bah = PF_FW_ARQBAH;
-       cq->rq.bal = PF_FW_ARQBAL;
-       cq->rq.len_mask = PF_FW_ARQLEN_ARQLEN_M;
-       cq->rq.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
-       cq->rq.head_mask = PF_FW_ARQH_ARQH_M;
+       ICE_CQ_INIT_REGS(cq, PF_FW);
 }
 
 /**
@@ -42,24 +46,7 @@ static void ice_mailbox_init_regs(struct ice_hw *hw)
 {
        struct ice_ctl_q_info *cq = &hw->mailboxq;
 
-       /* set head and tail registers in our local struct */
-       cq->sq.head = PF_MBX_ATQH;
-       cq->sq.tail = PF_MBX_ATQT;
-       cq->sq.len = PF_MBX_ATQLEN;
-       cq->sq.bah = PF_MBX_ATQBAH;
-       cq->sq.bal = PF_MBX_ATQBAL;
-       cq->sq.len_mask = PF_MBX_ATQLEN_ATQLEN_M;
-       cq->sq.len_ena_mask = PF_MBX_ATQLEN_ATQENABLE_M;
-       cq->sq.head_mask = PF_MBX_ATQH_ATQH_M;
-
-       cq->rq.head = PF_MBX_ARQH;
-       cq->rq.tail = PF_MBX_ARQT;
-       cq->rq.len = PF_MBX_ARQLEN;
-       cq->rq.bah = PF_MBX_ARQBAH;
-       cq->rq.bal = PF_MBX_ARQBAL;
-       cq->rq.len_mask = PF_MBX_ARQLEN_ARQLEN_M;
-       cq->rq.len_ena_mask = PF_MBX_ARQLEN_ARQENABLE_M;
-       cq->rq.head_mask = PF_MBX_ARQH_ARQH_M;
+       ICE_CQ_INIT_REGS(cq, PF_MBX);
 }
 
 /**
@@ -131,37 +118,20 @@ ice_alloc_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 }
 
 /**
- * ice_free_ctrlq_sq_ring - Free Control Transmit Queue (ATQ) rings
+ * ice_free_cq_ring - Free control queue ring
  * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
+ * @ring: pointer to the specific control queue ring
  *
- * This assumes the posted send buffers have already been cleaned
+ * This assumes the posted buffers have already been cleaned
  * and de-allocated
  */
-static void ice_free_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+static void ice_free_cq_ring(struct ice_hw *hw, struct ice_ctl_q_ring *ring)
 {
-       dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
-                          cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
-       cq->sq.desc_buf.va = NULL;
-       cq->sq.desc_buf.pa = 0;
-       cq->sq.desc_buf.size = 0;
-}
-
-/**
- * ice_free_ctrlq_rq_ring - Free Control Receive Queue (ARQ) rings
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- *
- * This assumes the posted receive buffers have already been cleaned
- * and de-allocated
- */
-static void ice_free_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
-{
-       dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.desc_buf.size,
-                          cq->rq.desc_buf.va, cq->rq.desc_buf.pa);
-       cq->rq.desc_buf.va = NULL;
-       cq->rq.desc_buf.pa = 0;
-       cq->rq.desc_buf.size = 0;
+       dmam_free_coherent(ice_hw_to_dev(hw), ring->desc_buf.size,
+                          ring->desc_buf.va, ring->desc_buf.pa);
+       ring->desc_buf.va = NULL;
+       ring->desc_buf.pa = 0;
+       ring->desc_buf.size = 0;
 }
 
 /**
@@ -280,54 +250,23 @@ unwind_alloc_sq_bufs:
        return ICE_ERR_NO_MEMORY;
 }
 
-/**
- * ice_free_rq_bufs - Free ARQ buffer info elements
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- */
-static void ice_free_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
-{
-       int i;
-
-       /* free descriptors */
-       for (i = 0; i < cq->num_rq_entries; i++) {
-               dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
-                                  cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
-               cq->rq.r.rq_bi[i].va = NULL;
-               cq->rq.r.rq_bi[i].pa = 0;
-               cq->rq.r.rq_bi[i].size = 0;
-       }
-
-       /* free the dma header */
-       devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
-}
-
-/**
- * ice_free_sq_bufs - Free ATQ buffer info elements
- * @hw: pointer to the hardware structure
- * @cq: pointer to the specific Control queue
- */
-static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+static enum ice_status
+ice_cfg_cq_regs(struct ice_hw *hw, struct ice_ctl_q_ring *ring, u16 num_entries)
 {
-       int i;
+       /* Clear Head and Tail */
+       wr32(hw, ring->head, 0);
+       wr32(hw, ring->tail, 0);
 
-       /* only unmap if the address is non-NULL */
-       for (i = 0; i < cq->num_sq_entries; i++)
-               if (cq->sq.r.sq_bi[i].pa) {
-                       dmam_free_coherent(ice_hw_to_dev(hw),
-                                          cq->sq.r.sq_bi[i].size,
-                                          cq->sq.r.sq_bi[i].va,
-                                          cq->sq.r.sq_bi[i].pa);
-                       cq->sq.r.sq_bi[i].va = NULL;
-                       cq->sq.r.sq_bi[i].pa = 0;
-                       cq->sq.r.sq_bi[i].size = 0;
-               }
+       /* set starting point */
+       wr32(hw, ring->len, (num_entries | ring->len_ena_mask));
+       wr32(hw, ring->bal, lower_32_bits(ring->desc_buf.pa));
+       wr32(hw, ring->bah, upper_32_bits(ring->desc_buf.pa));
 
-       /* free the buffer info list */
-       devm_kfree(ice_hw_to_dev(hw), cq->sq.cmd_buf);
+       /* Check one register to verify that config was applied */
+       if (rd32(hw, ring->bal) != lower_32_bits(ring->desc_buf.pa))
+               return ICE_ERR_AQ_ERROR;
 
-       /* free the dma header */
-       devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+       return 0;
 }
 
 /**
@@ -340,23 +279,7 @@ static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 static enum ice_status
 ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 {
-       u32 reg = 0;
-
-       /* Clear Head and Tail */
-       wr32(hw, cq->sq.head, 0);
-       wr32(hw, cq->sq.tail, 0);
-
-       /* set starting point */
-       wr32(hw, cq->sq.len, (cq->num_sq_entries | cq->sq.len_ena_mask));
-       wr32(hw, cq->sq.bal, lower_32_bits(cq->sq.desc_buf.pa));
-       wr32(hw, cq->sq.bah, upper_32_bits(cq->sq.desc_buf.pa));
-
-       /* Check one register to verify that config was applied */
-       reg = rd32(hw, cq->sq.bal);
-       if (reg != lower_32_bits(cq->sq.desc_buf.pa))
-               return ICE_ERR_AQ_ERROR;
-
-       return 0;
+       return ice_cfg_cq_regs(hw, &cq->sq, cq->num_sq_entries);
 }
 
 /**
@@ -369,25 +292,15 @@ ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 static enum ice_status
 ice_cfg_rq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
 {
-       u32 reg = 0;
-
-       /* Clear Head and Tail */
-       wr32(hw, cq->rq.head, 0);
-       wr32(hw, cq->rq.tail, 0);
+       enum ice_status status;
 
-       /* set starting point */
-       wr32(hw, cq->rq.len, (cq->num_rq_entries | cq->rq.len_ena_mask));
-       wr32(hw, cq->rq.bal, lower_32_bits(cq->rq.desc_buf.pa));
-       wr32(hw, cq->rq.bah, upper_32_bits(cq->rq.desc_buf.pa));
+       status = ice_cfg_cq_regs(hw, &cq->rq, cq->num_rq_entries);
+       if (status)
+               return status;
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, cq->rq.tail, (u32)(cq->num_rq_entries - 1));
 
-       /* Check one register to verify that config was applied */
-       reg = rd32(hw, cq->rq.bal);
-       if (reg != lower_32_bits(cq->rq.desc_buf.pa))
-               return ICE_ERR_AQ_ERROR;
-
        return 0;
 }
 
@@ -444,7 +357,7 @@ static enum ice_status ice_init_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        goto init_ctrlq_exit;
 
 init_ctrlq_free_rings:
-       ice_free_ctrlq_sq_ring(hw, cq);
+       ice_free_cq_ring(hw, &cq->sq);
 
 init_ctrlq_exit:
        return ret_code;
@@ -503,12 +416,33 @@ static enum ice_status ice_init_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        goto init_ctrlq_exit;
 
 init_ctrlq_free_rings:
-       ice_free_ctrlq_rq_ring(hw, cq);
+       ice_free_cq_ring(hw, &cq->rq);
 
 init_ctrlq_exit:
        return ret_code;
 }
 
+#define ICE_FREE_CQ_BUFS(hw, qi, ring)                                 \
+do {                                                                   \
+       int i;                                                          \
+       /* free descriptors */                                          \
+       for (i = 0; i < (qi)->num_##ring##_entries; i++)                \
+               if ((qi)->ring.r.ring##_bi[i].pa) {                     \
+                       dmam_free_coherent(ice_hw_to_dev(hw),           \
+                                          (qi)->ring.r.ring##_bi[i].size,\
+                                          (qi)->ring.r.ring##_bi[i].va,\
+                                          (qi)->ring.r.ring##_bi[i].pa);\
+                       (qi)->ring.r.ring##_bi[i].va = NULL;            \
+                       (qi)->ring.r.ring##_bi[i].pa = 0;               \
+                       (qi)->ring.r.ring##_bi[i].size = 0;             \
+               }                                                       \
+       /* free the buffer info list */                                 \
+       if ((qi)->ring.cmd_buf)                                         \
+               devm_kfree(ice_hw_to_dev(hw), (qi)->ring.cmd_buf);      \
+       /* free dma head */                                             \
+       devm_kfree(ice_hw_to_dev(hw), (qi)->ring.dma_head);             \
+} while (0)
+
 /**
  * ice_shutdown_sq - shutdown the Control ATQ
  * @hw: pointer to the hardware structure
@@ -538,8 +472,8 @@ ice_shutdown_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        cq->sq.count = 0;       /* to indicate uninitialized queue */
 
        /* free ring buffers and the ring itself */
-       ice_free_sq_bufs(hw, cq);
-       ice_free_ctrlq_sq_ring(hw, cq);
+       ICE_FREE_CQ_BUFS(hw, cq, sq);
+       ice_free_cq_ring(hw, &cq->sq);
 
 shutdown_sq_out:
        mutex_unlock(&cq->sq_lock);
@@ -606,8 +540,8 @@ ice_shutdown_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
        cq->rq.count = 0;
 
        /* free ring buffers and the ring itself */
-       ice_free_rq_bufs(hw, cq);
-       ice_free_ctrlq_rq_ring(hw, cq);
+       ICE_FREE_CQ_BUFS(hw, cq, rq);
+       ice_free_cq_ring(hw, &cq->rq);
 
 shutdown_rq_out:
        mutex_unlock(&cq->rq_lock);
@@ -657,7 +591,6 @@ init_ctrlq_free_rq:
  *     - cq->num_rq_entries
  *     - cq->rq_buf_size
  *     - cq->sq_buf_size
- *
  */
 static enum ice_status ice_init_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
 {
@@ -841,7 +774,7 @@ static bool ice_sq_done(struct ice_hw *hw, struct ice_ctl_q_info *cq)
  * @buf_size: size of buffer for indirect commands (or 0 for direct commands)
  * @cd: pointer to command details structure
  *
- * This is the main send command routine for the ATQ.  It runs the q,
+ * This is the main send command routine for the ATQ. It runs the queue,
  * cleans the queue, etc.
  */
 enum ice_status
@@ -1035,7 +968,7 @@ void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode)
  * @pending: number of events that could be left to process
  *
  * This function cleans one Admin Receive Queue element and returns
- * the contents through e.  It can also return how many events are
+ * the contents through e. It can also return how many events are
  * left to process through 'pending'.
  */
 enum ice_status
index 648acdb..3b6e387 100644 (file)
@@ -62,7 +62,7 @@ static const struct ice_stats ice_gstrings_vsi_stats[] = {
  * The PF_STATs are appended to the netdev stats only when ethtool -S
  * is queried on the base PF netdev.
  */
-static struct ice_stats ice_gstrings_pf_stats[] = {
+static const struct ice_stats ice_gstrings_pf_stats[] = {
        ICE_PF_STAT("tx_bytes", stats.eth.tx_bytes),
        ICE_PF_STAT("rx_bytes", stats.eth.rx_bytes),
        ICE_PF_STAT("tx_unicast", stats.eth.tx_unicast),
@@ -104,7 +104,7 @@ static struct ice_stats ice_gstrings_pf_stats[] = {
        ICE_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
 };
 
-static u32 ice_regs_dump_list[] = {
+static const u32 ice_regs_dump_list[] = {
        PFGEN_STATE,
        PRTGEN_STATUS,
        QRX_CTRL(0),
@@ -260,10 +260,10 @@ static int ice_get_sset_count(struct net_device *netdev, int sset)
                 * a private ethtool flag). This is due to the nature of the
                 * ethtool stats API.
                 *
-                * User space programs such as ethtool must make 3 separate
+                * Userspace programs such as ethtool must make 3 separate
                 * ioctl requests, one for size, one for the strings, and
                 * finally one for the stats. Since these cross into
-                * user space, changes to the number or size could result in
+                * userspace, changes to the number or size could result in
                 * undefined memory access or incorrect string<->value
                 * correlations for statistics.
                 *
@@ -1392,17 +1392,17 @@ static int ice_nway_reset(struct net_device *netdev)
 {
        /* restart autonegotiation */
        struct ice_netdev_priv *np = netdev_priv(netdev);
-       struct ice_link_status *hw_link_info;
        struct ice_vsi *vsi = np->vsi;
        struct ice_port_info *pi;
        enum ice_status status;
-       bool link_up;
 
        pi = vsi->port_info;
-       hw_link_info = &pi->phy.link_info;
-       link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+       /* If VSI state is up, then restart autoneg with link up */
+       if (!test_bit(__ICE_DOWN, vsi->back->state))
+               status = ice_aq_set_link_restart_an(pi, true, NULL);
+       else
+               status = ice_aq_set_link_restart_an(pi, false, NULL);
 
-       status = ice_aq_set_link_restart_an(pi, link_up, NULL);
        if (status) {
                netdev_info(netdev, "link restart failed, err %d aq_err %d\n",
                            status, pi->hw->adminq.sq_last_status);
@@ -1441,7 +1441,7 @@ ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
 /**
  * ice_set_pauseparam - Set Flow Control parameter
  * @netdev: network interface device structure
- * @pause: return tx/rx flow control status
+ * @pause: return Tx/Rx flow control status
  */
 static int
 ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
@@ -1543,7 +1543,7 @@ static u32 ice_get_rxfh_key_size(struct net_device __always_unused *netdev)
 }
 
 /**
- * ice_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * ice_get_rxfh_indir_size - get the Rx flow hash indirection table size
  * @netdev: network interface device structure
  *
  * Returns the table size.
@@ -1556,7 +1556,7 @@ static u32 ice_get_rxfh_indir_size(struct net_device *netdev)
 }
 
 /**
- * ice_get_rxfh - get the rx flow hash indirection table
+ * ice_get_rxfh - get the Rx flow hash indirection table
  * @netdev: network interface device structure
  * @indir: indirection table
  * @key: hash key
@@ -1603,7 +1603,7 @@ out:
 }
 
 /**
- * ice_set_rxfh - set the rx flow hash indirection table
+ * ice_set_rxfh - set the Rx flow hash indirection table
  * @netdev: network interface device structure
  * @indir: indirection table
  * @key: hash key
index 596b9fb..5507928 100644 (file)
@@ -7,6 +7,9 @@
 #define _ICE_HW_AUTOGEN_H_
 
 #define QTX_COMM_DBELL(_DBQM)                  (0x002C0000 + ((_DBQM) * 4))
+#define QTX_COMM_HEAD(_DBQM)                   (0x000E0000 + ((_DBQM) * 4))
+#define QTX_COMM_HEAD_HEAD_S                   0
+#define QTX_COMM_HEAD_HEAD_M                   ICE_M(0x1FFF, 0)
 #define PF_FW_ARQBAH                           0x00080180
 #define PF_FW_ARQBAL                           0x00080080
 #define PF_FW_ARQH                             0x00080380
index 7d2a667..bb51dd7 100644 (file)
@@ -6,11 +6,11 @@
 
 union ice_32byte_rx_desc {
        struct {
-               __le64  pkt_addr; /* Packet buffer address */
-               __le64  hdr_addr; /* Header buffer address */
+               __le64 pkt_addr; /* Packet buffer address */
+               __le64 hdr_addr; /* Header buffer address */
                        /* bit 0 of hdr_addr is DD bit */
-               __le64  rsvd1;
-               __le64  rsvd2;
+               __le64 rsvd1;
+               __le64 rsvd2;
        } read;
        struct {
                struct {
@@ -105,11 +105,11 @@ enum ice_rx_ptype_payload_layer {
  */
 union ice_32b_rx_flex_desc {
        struct {
-               __le64  pkt_addr; /* Packet buffer address */
-               __le64  hdr_addr; /* Header buffer address */
-                                 /* bit 0 of hdr_addr is DD bit */
-               __le64  rsvd1;
-               __le64  rsvd2;
+               __le64 pkt_addr; /* Packet buffer address */
+               __le64 hdr_addr; /* Header buffer address */
+                                /* bit 0 of hdr_addr is DD bit */
+               __le64 rsvd1;
+               __le64 rsvd2;
        } read;
        struct {
                /* Qword 0 */
@@ -256,6 +256,9 @@ enum ice_rx_flex_desc_status_error_0_bits {
 
 #define ICE_RXQ_CTX_SIZE_DWORDS                8
 #define ICE_RXQ_CTX_SZ                 (ICE_RXQ_CTX_SIZE_DWORDS * sizeof(u32))
+#define ICE_TX_CMPLTNQ_CTX_SIZE_DWORDS 22
+#define ICE_TX_DRBELL_Q_CTX_SIZE_DWORDS        5
+#define GLTCLAN_CQ_CNTX(i, CQ)         (GLTCLAN_CQ_CNTX0(CQ) + ((i) * 0x0800))
 
 /* RLAN Rx queue context data
  *
@@ -274,18 +277,18 @@ struct ice_rlan_ctx {
        u16 dbuf; /* bigger than needed, see above for reason */
 #define ICE_RLAN_CTX_HBUF_S 6
        u16 hbuf; /* bigger than needed, see above for reason */
-       u8  dtype;
-       u8  dsize;
-       u8  crcstrip;
-       u8  l2tsel;
-       u8  hsplit_0;
-       u8  hsplit_1;
-       u8  showiv;
+       u8 dtype;
+       u8 dsize;
+       u8 crcstrip;
+       u8 l2tsel;
+       u8 hsplit_0;
+       u8 hsplit_1;
+       u8 showiv;
        u32 rxmax; /* bigger than needed, see above for reason */
-       u8  tphrdesc_ena;
-       u8  tphwdesc_ena;
-       u8  tphdata_ena;
-       u8  tphhead_ena;
+       u8 tphrdesc_ena;
+       u8 tphwdesc_ena;
+       u8 tphdata_ena;
+       u8 tphhead_ena;
        u16 lrxqthresh; /* bigger than needed, see above for reason */
 };
 
@@ -413,35 +416,35 @@ enum ice_tx_ctx_desc_cmd_bits {
 struct ice_tlan_ctx {
 #define ICE_TLAN_CTX_BASE_S    7
        u64 base;               /* base is defined in 128-byte units */
-       u8  port_num;
+       u8 port_num;
        u16 cgd_num;            /* bigger than needed, see above for reason */
-       u8  pf_num;
+       u8 pf_num;
        u16 vmvf_num;
-       u8  vmvf_type;
+       u8 vmvf_type;
 #define ICE_TLAN_CTX_VMVF_TYPE_VF      0
 #define ICE_TLAN_CTX_VMVF_TYPE_VMQ     1
 #define ICE_TLAN_CTX_VMVF_TYPE_PF      2
        u16 src_vsi;
-       u8  tsyn_ena;
-       u8  alt_vlan;
+       u8 tsyn_ena;
+       u8 alt_vlan;
        u16 cpuid;              /* bigger than needed, see above for reason */
-       u8  wb_mode;
-       u8  tphrd_desc;
-       u8  tphrd;
-       u8  tphwr_desc;
+       u8 wb_mode;
+       u8 tphrd_desc;
+       u8 tphrd;
+       u8 tphwr_desc;
        u16 cmpq_id;
        u16 qnum_in_func;
-       u8  itr_notification_mode;
-       u8  adjust_prof_id;
+       u8 itr_notification_mode;
+       u8 adjust_prof_id;
        u32 qlen;               /* bigger than needed, see above for reason */
-       u8  quanta_prof_idx;
-       u8  tso_ena;
+       u8 quanta_prof_idx;
+       u8 tso_ena;
        u16 tso_qnum;
-       u8  legacy_int;
-       u8  drop_ena;
-       u8  cache_prof_idx;
-       u8  pkt_shaper_prof_idx;
-       u8  int_q_state;        /* width not needed - internal do not write */
+       u8 legacy_int;
+       u8 drop_ena;
+       u8 cache_prof_idx;
+       u8 pkt_shaper_prof_idx;
+       u8 int_q_state; /* width not needed - internal do not write */
 };
 
 /* macro to make the table lines short */
index 1041fa2..29b1dcf 100644 (file)
@@ -20,7 +20,7 @@ static int ice_setup_rx_ctx(struct ice_ring *ring)
        u16 pf_q;
        int err;
 
-       /* what is RX queue number in global space of 2K Rx queues */
+       /* what is Rx queue number in global space of 2K Rx queues */
        pf_q = vsi->rxq_map[ring->q_index];
 
        /* clear the context structure first */
@@ -174,15 +174,15 @@ static int ice_pf_rxq_wait(struct ice_pf *pf, int pf_q, bool ena)
 {
        int i;
 
-       for (i = 0; i < ICE_Q_WAIT_RETRY_LIMIT; i++) {
+       for (i = 0; i < ICE_Q_WAIT_MAX_RETRY; i++) {
                u32 rx_reg = rd32(&pf->hw, QRX_CTRL(pf_q));
 
                if (ena == !!(rx_reg & QRX_CTRL_QENA_STAT_M))
                        break;
 
-               usleep_range(10, 20);
+               usleep_range(20, 40);
        }
-       if (i >= ICE_Q_WAIT_RETRY_LIMIT)
+       if (i >= ICE_Q_WAIT_MAX_RETRY)
                return -ETIMEDOUT;
 
        return 0;
@@ -774,11 +774,13 @@ static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt)
  */
 static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
 {
-       u16 offset = 0, qmap = 0, numq_tc;
-       u16 pow = 0, max_rss = 0, qcount;
+       u16 offset = 0, qmap = 0, tx_count = 0;
        u16 qcount_tx = vsi->alloc_txq;
        u16 qcount_rx = vsi->alloc_rxq;
+       u16 tx_numq_tc, rx_numq_tc;
+       u16 pow = 0, max_rss = 0;
        bool ena_tc0 = false;
+       u8 netdev_tc = 0;
        int i;
 
        /* at least TC0 should be enabled by default */
@@ -794,7 +796,12 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
                vsi->tc_cfg.ena_tc |= 1;
        }
 
-       numq_tc = qcount_rx / vsi->tc_cfg.numtc;
+       rx_numq_tc = qcount_rx / vsi->tc_cfg.numtc;
+       if (!rx_numq_tc)
+               rx_numq_tc = 1;
+       tx_numq_tc = qcount_tx / vsi->tc_cfg.numtc;
+       if (!tx_numq_tc)
+               tx_numq_tc = 1;
 
        /* TC mapping is a function of the number of Rx queues assigned to the
         * VSI for each traffic class and the offset of these queues.
@@ -808,7 +815,8 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
         * Setup number and offset of Rx queues for all TCs for the VSI
         */
 
-       qcount = numq_tc;
+       qcount_rx = rx_numq_tc;
+
        /* qcount will change if RSS is enabled */
        if (test_bit(ICE_FLAG_RSS_ENA, vsi->back->flags)) {
                if (vsi->type == ICE_VSI_PF || vsi->type == ICE_VSI_VF) {
@@ -816,37 +824,41 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
                                max_rss = ICE_MAX_LG_RSS_QS;
                        else
                                max_rss = ICE_MAX_SMALL_RSS_QS;
-                       qcount = min_t(int, numq_tc, max_rss);
-                       qcount = min_t(int, qcount, vsi->rss_size);
+                       qcount_rx = min_t(int, rx_numq_tc, max_rss);
+                       qcount_rx = min_t(int, qcount_rx, vsi->rss_size);
                }
        }
 
        /* find the (rounded up) power-of-2 of qcount */
-       pow = order_base_2(qcount);
+       pow = order_base_2(qcount_rx);
 
        for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
                if (!(vsi->tc_cfg.ena_tc & BIT(i))) {
                        /* TC is not enabled */
                        vsi->tc_cfg.tc_info[i].qoffset = 0;
-                       vsi->tc_cfg.tc_info[i].qcount = 1;
+                       vsi->tc_cfg.tc_info[i].qcount_rx = 1;
+                       vsi->tc_cfg.tc_info[i].qcount_tx = 1;
+                       vsi->tc_cfg.tc_info[i].netdev_tc = 0;
                        ctxt->info.tc_mapping[i] = 0;
                        continue;
                }
 
                /* TC is enabled */
                vsi->tc_cfg.tc_info[i].qoffset = offset;
-               vsi->tc_cfg.tc_info[i].qcount = qcount;
+               vsi->tc_cfg.tc_info[i].qcount_rx = qcount_rx;
+               vsi->tc_cfg.tc_info[i].qcount_tx = tx_numq_tc;
+               vsi->tc_cfg.tc_info[i].netdev_tc = netdev_tc++;
 
                qmap = ((offset << ICE_AQ_VSI_TC_Q_OFFSET_S) &
                        ICE_AQ_VSI_TC_Q_OFFSET_M) |
                        ((pow << ICE_AQ_VSI_TC_Q_NUM_S) &
                         ICE_AQ_VSI_TC_Q_NUM_M);
-               offset += qcount;
+               offset += qcount_rx;
+               tx_count += tx_numq_tc;
                ctxt->info.tc_mapping[i] = cpu_to_le16(qmap);
        }
-
-       vsi->num_txq = qcount_tx;
        vsi->num_rxq = offset;
+       vsi->num_txq = tx_count;
 
        if (vsi->type == ICE_VSI_VF && vsi->num_txq != vsi->num_rxq) {
                dev_dbg(&vsi->back->pdev->dev, "VF VSI should have same number of Tx and Rx queues. Hence making them equal\n");
@@ -1000,7 +1012,7 @@ void ice_vsi_free_q_vectors(struct ice_vsi *vsi)
  * @vsi: the VSI being configured
  * @v_idx: index of the vector in the VSI struct
  *
- * We allocate one q_vector.  If allocation fails we return -ENOMEM.
+ * We allocate one q_vector. If allocation fails we return -ENOMEM.
  */
 static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, int v_idx)
 {
@@ -1039,7 +1051,7 @@ out:
  * ice_vsi_alloc_q_vectors - Allocate memory for interrupt vectors
  * @vsi: the VSI being configured
  *
- * We allocate one q_vector per queue interrupt.  If allocation fails we
+ * We allocate one q_vector per queue interrupt. If allocation fails we
  * return -ENOMEM.
  */
 static int ice_vsi_alloc_q_vectors(struct ice_vsi *vsi)
@@ -1188,7 +1200,7 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
        struct ice_pf *pf = vsi->back;
        int i;
 
-       /* Allocate tx_rings */
+       /* Allocate Tx rings */
        for (i = 0; i < vsi->alloc_txq; i++) {
                struct ice_ring *ring;
 
@@ -1207,7 +1219,7 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
                vsi->tx_rings[i] = ring;
        }
 
-       /* Allocate rx_rings */
+       /* Allocate Rx rings */
        for (i = 0; i < vsi->alloc_rxq; i++) {
                struct ice_ring *ring;
 
@@ -1611,55 +1623,62 @@ int ice_vsi_cfg_txqs(struct ice_vsi *vsi)
        struct ice_aqc_add_tx_qgrp *qg_buf;
        struct ice_aqc_add_txqs_perq *txq;
        struct ice_pf *pf = vsi->back;
+       u8 num_q_grps, q_idx = 0;
        enum ice_status status;
        u16 buf_len, i, pf_q;
        int err = 0, tc = 0;
-       u8 num_q_grps;
 
        buf_len = sizeof(struct ice_aqc_add_tx_qgrp);
        qg_buf = devm_kzalloc(&pf->pdev->dev, buf_len, GFP_KERNEL);
        if (!qg_buf)
                return -ENOMEM;
 
-       if (vsi->num_txq > ICE_MAX_TXQ_PER_TXQG) {
-               err = -EINVAL;
-               goto err_cfg_txqs;
-       }
        qg_buf->num_txqs = 1;
        num_q_grps = 1;
 
-       /* set up and configure the Tx queues */
-       ice_for_each_txq(vsi, i) {
-               struct ice_tlan_ctx tlan_ctx = { 0 };
+       /* set up and configure the Tx queues for each enabled TC */
+       for (tc = 0; tc < ICE_MAX_TRAFFIC_CLASS; tc++) {
+               if (!(vsi->tc_cfg.ena_tc & BIT(tc)))
+                       break;
 
-               pf_q = vsi->txq_map[i];
-               ice_setup_tx_ctx(vsi->tx_rings[i], &tlan_ctx, pf_q);
-               /* copy context contents into the qg_buf */
-               qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
-               ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
-                           ice_tlan_ctx_info);
+               for (i = 0; i < vsi->tc_cfg.tc_info[tc].qcount_tx; i++) {
+                       struct ice_tlan_ctx tlan_ctx = { 0 };
+
+                       pf_q = vsi->txq_map[q_idx];
+                       ice_setup_tx_ctx(vsi->tx_rings[q_idx], &tlan_ctx,
+                                        pf_q);
+                       /* copy context contents into the qg_buf */
+                       qg_buf->txqs[0].txq_id = cpu_to_le16(pf_q);
+                       ice_set_ctx((u8 *)&tlan_ctx, qg_buf->txqs[0].txq_ctx,
+                                   ice_tlan_ctx_info);
+
+                       /* init queue specific tail reg. It is referred as
+                        * transmit comm scheduler queue doorbell.
+                        */
+                       vsi->tx_rings[q_idx]->tail =
+                               pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
+                       status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc,
+                                                num_q_grps, qg_buf, buf_len,
+                                                NULL);
+                       if (status) {
+                               dev_err(&vsi->back->pdev->dev,
+                                       "Failed to set LAN Tx queue context, error: %d\n",
+                                       status);
+                               err = -ENODEV;
+                               goto err_cfg_txqs;
+                       }
 
-               /* init queue specific tail reg. It is referred as transmit
-                * comm scheduler queue doorbell.
-                */
-               vsi->tx_rings[i]->tail = pf->hw.hw_addr + QTX_COMM_DBELL(pf_q);
-               status = ice_ena_vsi_txq(vsi->port_info, vsi->idx, tc,
-                                        num_q_grps, qg_buf, buf_len, NULL);
-               if (status) {
-                       dev_err(&vsi->back->pdev->dev,
-                               "Failed to set LAN Tx queue context, error: %d\n",
-                               status);
-                       err = -ENODEV;
-                       goto err_cfg_txqs;
-               }
+                       /* Add Tx Queue TEID into the VSI Tx ring from the
+                        * response. This will complete configuring and
+                        * enabling the queue.
+                        */
+                       txq = &qg_buf->txqs[0];
+                       if (pf_q == le16_to_cpu(txq->txq_id))
+                               vsi->tx_rings[q_idx]->txq_teid =
+                                       le32_to_cpu(txq->q_teid);
 
-               /* Add Tx Queue TEID into the VSI Tx ring from the response
-                * This will complete configuring and enabling the queue.
-                */
-               txq = &qg_buf->txqs[0];
-               if (pf_q == le16_to_cpu(txq->txq_id))
-                       vsi->tx_rings[i]->txq_teid =
-                               le32_to_cpu(txq->q_teid);
+                       q_idx++;
+               }
        }
 err_cfg_txqs:
        devm_kfree(&pf->pdev->dev, qg_buf);
@@ -1908,7 +1927,8 @@ int ice_vsi_stop_tx_rings(struct ice_vsi *vsi, enum ice_disq_rst_src rst_src,
        ice_for_each_txq(vsi, i) {
                u16 v_idx;
 
-               if (!vsi->tx_rings || !vsi->tx_rings[i]) {
+               if (!vsi->tx_rings || !vsi->tx_rings[i] ||
+                   !vsi->tx_rings[i]->q_vector) {
                        err = -EINVAL;
                        goto err_out;
                }
@@ -2056,6 +2076,9 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
        /* set RSS capabilities */
        ice_vsi_set_rss_params(vsi);
 
+       /* set tc configuration */
+       ice_vsi_set_tc_cfg(vsi);
+
        /* create the VSI */
        ret = ice_vsi_init(vsi);
        if (ret)
@@ -2113,17 +2136,13 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
                pf->q_left_rx -= vsi->alloc_rxq;
                break;
        default:
-               /* if VSI type is not recognized, clean up the resources and
-                * exit
-                */
+               /* clean up the resources and exit */
                goto unroll_vsi_init;
        }
 
-       ice_vsi_set_tc_cfg(vsi);
-
        /* configure VSI nodes based on number of queues and TC's */
        for (i = 0; i < vsi->tc_cfg.numtc; i++)
-               max_txqs[i] = vsi->num_txq;
+               max_txqs[i] = pf->num_lan_tx;
 
        ret = ice_cfg_vsi_lan(vsi->port_info, vsi->idx, vsi->tc_cfg.ena_tc,
                              max_txqs);
@@ -2314,7 +2333,7 @@ static int ice_search_res(struct ice_res_tracker *res, u16 needed, u16 id)
        int start = res->search_hint;
        int end = start;
 
-       if ((start + needed) >  res->num_entries)
+       if ((start + needed) > res->num_entries)
                return -ENOMEM;
 
        id |= ICE_RES_VALID_BIT;
@@ -2491,6 +2510,7 @@ int ice_vsi_release(struct ice_vsi *vsi)
        }
 
        ice_remove_vsi_fltr(&pf->hw, vsi->idx);
+       ice_rm_vsi_lan_cfg(vsi->port_info, vsi->idx);
        ice_vsi_delete(vsi);
        ice_vsi_free_q_vectors(vsi);
        ice_vsi_clear_rings(vsi);
@@ -2518,11 +2538,14 @@ int ice_vsi_release(struct ice_vsi *vsi)
 int ice_vsi_rebuild(struct ice_vsi *vsi)
 {
        u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
+       struct ice_pf *pf;
        int ret, i;
 
        if (!vsi)
                return -EINVAL;
 
+       pf = vsi->back;
+       ice_rm_vsi_lan_cfg(vsi->port_info, vsi->idx);
        ice_vsi_free_q_vectors(vsi);
        ice_free_res(vsi->back->sw_irq_tracker, vsi->sw_base_vector, vsi->idx);
        ice_free_res(vsi->back->hw_irq_tracker, vsi->hw_base_vector, vsi->idx);
@@ -2532,6 +2555,7 @@ int ice_vsi_rebuild(struct ice_vsi *vsi)
        ice_vsi_free_arrays(vsi, false);
        ice_dev_onetime_setup(&vsi->back->hw);
        ice_vsi_set_num_qs(vsi);
+       ice_vsi_set_tc_cfg(vsi);
 
        /* Initialize VSI struct elements and create VSI in FW */
        ret = ice_vsi_init(vsi);
@@ -2578,11 +2602,9 @@ int ice_vsi_rebuild(struct ice_vsi *vsi)
                break;
        }
 
-       ice_vsi_set_tc_cfg(vsi);
-
        /* configure VSI nodes based on number of queues and TC's */
        for (i = 0; i < vsi->tc_cfg.numtc; i++)
-               max_txqs[i] = vsi->num_txq;
+               max_txqs[i] = pf->num_lan_tx;
 
        ret = ice_cfg_vsi_lan(vsi->port_info, vsi->idx, vsi->tc_cfg.ena_tc,
                              max_txqs);
index 333312a..e45e574 100644 (file)
@@ -349,6 +349,9 @@ ice_prepare_for_reset(struct ice_pf *pf)
        /* disable the VSIs and their queues that are not already DOWN */
        ice_pf_dis_all_vsi(pf);
 
+       if (hw->port_info)
+               ice_sched_clear_port(hw->port_info);
+
        ice_shutdown_all_ctrlq(hw);
 
        set_bit(__ICE_PREPARED_FOR_RESET, pf->state);
@@ -405,7 +408,7 @@ static void ice_reset_subtask(struct ice_pf *pf)
        /* When a CORER/GLOBR/EMPR is about to happen, the hardware triggers an
         * OICR interrupt. The OICR handler (ice_misc_intr) determines what type
         * of reset is pending and sets bits in pf->state indicating the reset
-        * type and __ICE_RESET_OICR_RECV.  So, if the latter bit is set
+        * type and __ICE_RESET_OICR_RECV. So, if the latter bit is set
         * prepare for pending reset if not already (for PF software-initiated
         * global resets the software should already be prepared for it as
         * indicated by __ICE_PREPARED_FOR_RESET; for global resets initiated
@@ -1379,7 +1382,7 @@ static void ice_free_irq_msix_misc(struct ice_pf *pf)
  * @pf: board private structure
  *
  * This sets up the handler for MSIX 0, which is used to manage the
- * non-queue interrupts, e.g. AdminQ and errors.  This is not used
+ * non-queue interrupts, e.g. AdminQ and errors. This is not used
  * when in MSI or Legacy interrupt mode.
  */
 static int ice_req_irq_msix_misc(struct ice_pf *pf)
@@ -1783,7 +1786,7 @@ static void ice_determine_q_usage(struct ice_pf *pf)
 
        pf->num_lan_tx = min_t(int, q_left_tx, num_online_cpus());
 
-       /* only 1 rx queue unless RSS is enabled */
+       /* only 1 Rx queue unless RSS is enabled */
        if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags))
                pf->num_lan_rx = 1;
        else
@@ -2091,8 +2094,7 @@ static int ice_probe(struct pci_dev *pdev,
 
        ice_determine_q_usage(pf);
 
-       pf->num_alloc_vsi = min_t(u16, ICE_MAX_VSI_ALLOC,
-                                 hw->func_caps.guaranteed_num_vsi);
+       pf->num_alloc_vsi = hw->func_caps.guar_num_vsi;
        if (!pf->num_alloc_vsi) {
                err = -EIO;
                goto err_init_pf_unroll;
@@ -2544,7 +2546,6 @@ static int ice_vsi_cfg(struct ice_vsi *vsi)
                if (err)
                        return err;
        }
-
        err = ice_vsi_cfg_txqs(vsi);
        if (!err)
                err = ice_vsi_cfg_rxqs(vsi);
@@ -3138,8 +3139,9 @@ static void ice_vsi_release_all(struct ice_pf *pf)
 /**
  * ice_dis_vsi - pause a VSI
  * @vsi: the VSI being paused
+ * @locked: is the rtnl_lock already held
  */
-static void ice_dis_vsi(struct ice_vsi *vsi)
+static void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
 {
        if (test_bit(__ICE_DOWN, vsi->state))
                return;
@@ -3148,9 +3150,13 @@ static void ice_dis_vsi(struct ice_vsi *vsi)
 
        if (vsi->type == ICE_VSI_PF && vsi->netdev) {
                if (netif_running(vsi->netdev)) {
-                       rtnl_lock();
-                       vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
-                       rtnl_unlock();
+                       if (!locked) {
+                               rtnl_lock();
+                               vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
+                               rtnl_unlock();
+                       } else {
+                               vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
+                       }
                } else {
                        ice_vsi_close(vsi);
                }
@@ -3189,7 +3195,7 @@ static void ice_pf_dis_all_vsi(struct ice_pf *pf)
 
        ice_for_each_vsi(pf, v)
                if (pf->vsi[v])
-                       ice_dis_vsi(pf->vsi[v]);
+                       ice_dis_vsi(pf->vsi[v], false);
 }
 
 /**
@@ -3668,7 +3674,7 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
                 */
                status = ice_update_sw_rule_bridge_mode(hw);
                if (status) {
-                       netdev_err(dev, "update SW_RULE for bridge mode failed,  = %d err %d aq_err %d\n",
+                       netdev_err(dev, "switch rule update failed, mode = %d err %d aq_err %d\n",
                                   mode, status, hw->adminq.sq_last_status);
                        /* revert hw->evb_veb */
                        hw->evb_veb = (pf_sw->bridge_mode == BRIDGE_MODE_VEB);
@@ -3691,40 +3697,36 @@ static void ice_tx_timeout(struct net_device *netdev)
        struct ice_ring *tx_ring = NULL;
        struct ice_vsi *vsi = np->vsi;
        struct ice_pf *pf = vsi->back;
-       u32 head, val = 0, i;
        int hung_queue = -1;
+       u32 i;
 
        pf->tx_timeout_count++;
 
-       /* find the stopped queue the same way the stack does */
+       /* find the stopped queue the same way dev_watchdog() does */
        for (i = 0; i < netdev->num_tx_queues; i++) {
-               struct netdev_queue *q;
                unsigned long trans_start;
+               struct netdev_queue *q;
 
                q = netdev_get_tx_queue(netdev, i);
                trans_start = q->trans_start;
                if (netif_xmit_stopped(q) &&
                    time_after(jiffies,
-                              (trans_start + netdev->watchdog_timeo))) {
+                              trans_start + netdev->watchdog_timeo)) {
                        hung_queue = i;
                        break;
                }
        }
 
-       if (i == netdev->num_tx_queues) {
+       if (i == netdev->num_tx_queues)
                netdev_info(netdev, "tx_timeout: no netdev hung queue found\n");
-       } else {
+       else
                /* now that we have an index, find the tx_ring struct */
-               for (i = 0; i < vsi->num_txq; i++) {
-                       if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
-                               if (hung_queue ==
-                                   vsi->tx_rings[i]->q_index) {
+               for (i = 0; i < vsi->num_txq; i++)
+                       if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc)
+                               if (hung_queue == vsi->tx_rings[i]->q_index) {
                                        tx_ring = vsi->tx_rings[i];
                                        break;
                                }
-                       }
-               }
-       }
 
        /* Reset recovery level if enough time has elapsed after last timeout.
         * Also ensure no new reset action happens before next timeout period.
@@ -3736,17 +3738,20 @@ static void ice_tx_timeout(struct net_device *netdev)
                return;
 
        if (tx_ring) {
-               head = tx_ring->next_to_clean;
+               struct ice_hw *hw = &pf->hw;
+               u32 head, val = 0;
+
+               head = (rd32(hw, QTX_COMM_HEAD(vsi->txq_map[hung_queue])) &
+                       QTX_COMM_HEAD_HEAD_M) >> QTX_COMM_HEAD_HEAD_S;
                /* Read interrupt register */
                if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
-                       val = rd32(&pf->hw,
+                       val = rd32(hw,
                                   GLINT_DYN_CTL(tx_ring->q_vector->v_idx +
-                                       tx_ring->vsi->hw_base_vector));
+                                                tx_ring->vsi->hw_base_vector));
 
-               netdev_info(netdev, "tx_timeout: VSI_num: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n",
+               netdev_info(netdev, "tx_timeout: VSI_num: %d, Q %d, NTC: 0x%x, HW_HEAD: 0x%x, NTU: 0x%x, INT: 0x%x\n",
                            vsi->vsi_num, hung_queue, tx_ring->next_to_clean,
-                           head, tx_ring->next_to_use,
-                           readl(tx_ring->tail), val);
+                           head, tx_ring->next_to_use, val);
        }
 
        pf->tx_timeout_last_recovery = jiffies;
@@ -3780,7 +3785,7 @@ static void ice_tx_timeout(struct net_device *netdev)
  * @netdev: network interface device structure
  *
  * The open entry point is called when a network interface is made
- * active by the system (IFF_UP).  At this point all resources needed
+ * 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 netdev watchdog is enabled,
  * and the stack is notified that the interface is ready.
@@ -3813,7 +3818,7 @@ static int ice_open(struct net_device *netdev)
  * @netdev: network interface device structure
  *
  * The stop entry point is called when an interface is de-activated by the OS,
- * and the netdevice enters the DOWN state.  The hardware is still under the
+ * and the netdevice enters the DOWN state. The hardware is still under the
  * driver's control, but the netdev interface is disabled.
  *
  * Returns success only - not allowed to fail
@@ -3842,14 +3847,14 @@ ice_features_check(struct sk_buff *skb,
        size_t len;
 
        /* No point in doing any of this if neither checksum nor GSO are
-        * being requested for this frame.  We can rule out both by just
+        * being requested for this frame. We can rule out both by just
         * checking for CHECKSUM_PARTIAL
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL)
                return features;
 
        /* We cannot support GSO if the MSS is going to be less than
-        * 64 bytes.  If it is then we need to drop support for GSO.
+        * 64 bytes. If it is then we need to drop support for GSO.
         */
        if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_size < 64))
                features &= ~NETIF_F_GSO_MASK;
index 7cc8aa1..a168185 100644 (file)
@@ -630,7 +630,7 @@ static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
  *
  * Cleanup scheduling elements from SW DB
  */
-static void ice_sched_clear_port(struct ice_port_info *pi)
+void ice_sched_clear_port(struct ice_port_info *pi)
 {
        if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
                return;
@@ -894,8 +894,7 @@ static u8 ice_sched_get_vsi_layer(struct ice_hw *hw)
  * This function removes the leaf node that was created by the FW
  * during initialization
  */
-static void
-ice_rm_dflt_leaf_node(struct ice_port_info *pi)
+static void ice_rm_dflt_leaf_node(struct ice_port_info *pi)
 {
        struct ice_sched_node *node;
 
@@ -923,8 +922,7 @@ ice_rm_dflt_leaf_node(struct ice_port_info *pi)
  * This function frees all the nodes except root and TC that were created by
  * the FW during initialization
  */
-static void
-ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
+static void ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
 {
        struct ice_sched_node *node;
 
@@ -1339,7 +1337,7 @@ ice_sched_rm_vsi_child_nodes(struct ice_port_info *pi,
  * @num_nodes: pointer to num nodes array
  *
  * This function calculates the number of supported nodes needed to add this
- * VSI into tx tree including the VSI, parent and intermediate nodes in below
+ * VSI into Tx tree including the VSI, parent and intermediate nodes in below
  * layers
  */
 static void
@@ -1376,13 +1374,13 @@ ice_sched_calc_vsi_support_nodes(struct ice_hw *hw,
 }
 
 /**
- * ice_sched_add_vsi_support_nodes - add VSI supported nodes into tx tree
+ * ice_sched_add_vsi_support_nodes - add VSI supported nodes into Tx tree
  * @pi: port information structure
  * @vsi_handle: software VSI handle
  * @tc_node: pointer to TC node
  * @num_nodes: pointer to num nodes array
  *
- * This function adds the VSI supported nodes into tx tree including the
+ * This function adds the VSI supported nodes into Tx tree including the
  * VSI, its parent and intermediate nodes in below layers
  */
 static enum ice_status
@@ -1527,7 +1525,7 @@ ice_sched_update_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
 }
 
 /**
- * ice_sched_cfg_vsi - configure the new/exisiting VSI
+ * ice_sched_cfg_vsi - configure the new/existing VSI
  * @pi: port information structure
  * @vsi_handle: software VSI handle
  * @tc: TC number
@@ -1605,3 +1603,109 @@ ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
 
        return status;
 }
+
+/**
+ * ice_sched_rm_agg_vsi_entry - remove agg related VSI info entry
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ *
+ * This function removes single aggregator VSI info entry from
+ * aggregator list.
+ */
+static void
+ice_sched_rm_agg_vsi_info(struct ice_port_info *pi, u16 vsi_handle)
+{
+       struct ice_sched_agg_info *agg_info;
+       struct ice_sched_agg_info *atmp;
+
+       list_for_each_entry_safe(agg_info, atmp, &pi->agg_list, list_entry) {
+               struct ice_sched_agg_vsi_info *agg_vsi_info;
+               struct ice_sched_agg_vsi_info *vtmp;
+
+               list_for_each_entry_safe(agg_vsi_info, vtmp,
+                                        &agg_info->agg_vsi_list, list_entry)
+                       if (agg_vsi_info->vsi_handle == vsi_handle) {
+                               list_del(&agg_vsi_info->list_entry);
+                               devm_kfree(ice_hw_to_dev(pi->hw),
+                                          agg_vsi_info);
+                               return;
+                       }
+       }
+}
+
+/**
+ * ice_sched_rm_vsi_cfg - remove the VSI and its children nodes
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ * @owner: LAN or RDMA
+ *
+ * This function removes the VSI and its LAN or RDMA children nodes from the
+ * scheduler tree.
+ */
+static enum ice_status
+ice_sched_rm_vsi_cfg(struct ice_port_info *pi, u16 vsi_handle, u8 owner)
+{
+       enum ice_status status = ICE_ERR_PARAM;
+       struct ice_vsi_ctx *vsi_ctx;
+       u8 i, j = 0;
+
+       if (!ice_is_vsi_valid(pi->hw, vsi_handle))
+               return status;
+       mutex_lock(&pi->sched_lock);
+       vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
+       if (!vsi_ctx)
+               goto exit_sched_rm_vsi_cfg;
+
+       for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
+               struct ice_sched_node *vsi_node, *tc_node;
+
+               tc_node = ice_sched_get_tc_node(pi, i);
+               if (!tc_node)
+                       continue;
+
+               vsi_node = ice_sched_get_vsi_node(pi->hw, tc_node, vsi_handle);
+               if (!vsi_node)
+                       continue;
+
+               while (j < vsi_node->num_children) {
+                       if (vsi_node->children[j]->owner == owner) {
+                               ice_free_sched_node(pi, vsi_node->children[j]);
+
+                               /* reset the counter again since the num
+                                * children will be updated after node removal
+                                */
+                               j = 0;
+                       } else {
+                               j++;
+                       }
+               }
+               /* remove the VSI if it has no children */
+               if (!vsi_node->num_children) {
+                       ice_free_sched_node(pi, vsi_node);
+                       vsi_ctx->sched.vsi_node[i] = NULL;
+
+                       /* clean up agg related vsi info if any */
+                       ice_sched_rm_agg_vsi_info(pi, vsi_handle);
+               }
+               if (owner == ICE_SCHED_NODE_OWNER_LAN)
+                       vsi_ctx->sched.max_lanq[i] = 0;
+       }
+       status = 0;
+
+exit_sched_rm_vsi_cfg:
+       mutex_unlock(&pi->sched_lock);
+       return status;
+}
+
+/**
+ * ice_rm_vsi_lan_cfg - remove VSI and its LAN children nodes
+ * @pi: port information structure
+ * @vsi_handle: software VSI handle
+ *
+ * This function clears the VSI and its LAN children nodes from scheduler tree
+ * for all TCs.
+ */
+enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle)
+{
+       return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_LAN);
+}
index 5dc9cfa..da5b4c1 100644 (file)
@@ -12,6 +12,7 @@
 struct ice_sched_agg_vsi_info {
        struct list_head list_entry;
        DECLARE_BITMAP(tc_bitmap, ICE_MAX_TRAFFIC_CLASS);
+       u16 vsi_handle;
 };
 
 struct ice_sched_agg_info {
@@ -25,6 +26,7 @@ struct ice_sched_agg_info {
 /* FW AQ command calls */
 enum ice_status ice_sched_init_port(struct ice_port_info *pi);
 enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw);
+void ice_sched_clear_port(struct ice_port_info *pi);
 void ice_sched_cleanup_all(struct ice_hw *hw);
 struct ice_sched_node *
 ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid);
@@ -39,4 +41,5 @@ ice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
 enum ice_status
 ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
                  u8 owner, bool enable);
+enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle);
 #endif /* _ICE_SCHED_H_ */
index 027eba4..533b989 100644 (file)
@@ -46,7 +46,7 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval,
  * @link_speed: variable containing the link_speed to be converted
  *
  * Convert link speed supported by HW to link speed supported by virtchnl.
- * If adv_link_support is true, then return link speed in Mbps.  Else return
+ * If adv_link_support is true, then return link speed in Mbps. Else return
  * link speed as a VIRTCHNL_LINK_SPEED_* casted to a u32. Note that the caller
  * needs to cast back to an enum virtchnl_link_speed in the case where
  * adv_link_support is false, but when adv_link_support is true the caller can
index 40c9c65..2e56931 100644 (file)
@@ -92,8 +92,7 @@ ice_aq_alloc_free_res(struct ice_hw *hw, u16 num_entries,
  * Allocate memory for the entire recipe table and initialize the structures/
  * entries corresponding to basic recipes.
  */
-enum ice_status
-ice_init_def_sw_recp(struct ice_hw *hw)
+enum ice_status ice_init_def_sw_recp(struct ice_hw *hw)
 {
        struct ice_sw_recipe *recps;
        u8 i;
@@ -130,7 +129,7 @@ ice_init_def_sw_recp(struct ice_hw *hw)
  *
  * NOTE: *req_desc is both an input/output parameter.
  * The caller of this function first calls this function with *request_desc set
- * to 0.  If the response from f/w has *req_desc set to 0, all the switch
+ * to 0. If the response from f/w has *req_desc set to 0, all the switch
  * configuration information has been returned; if non-zero (meaning not all
  * the information was returned), the caller should call this function again
  * with *req_desc set to the previous value returned by f/w to get the
@@ -629,25 +628,36 @@ enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw)
 /**
  * ice_fill_sw_info - Helper function to populate lb_en and lan_en
  * @hw: pointer to the hardware structure
- * @f_info: filter info structure to fill/update
+ * @fi: filter info structure to fill/update
  *
  * This helper function populates the lb_en and lan_en elements of the provided
  * ice_fltr_info struct using the switch's type and characteristics of the
  * switch rule being configured.
  */
-static void ice_fill_sw_info(struct ice_hw *hw, struct ice_fltr_info *f_info)
+static void ice_fill_sw_info(struct ice_hw *hw, struct ice_fltr_info *fi)
 {
-       f_info->lb_en = false;
-       f_info->lan_en = false;
-       if ((f_info->flag & ICE_FLTR_TX) &&
-           (f_info->fltr_act == ICE_FWD_TO_VSI ||
-            f_info->fltr_act == ICE_FWD_TO_VSI_LIST ||
-            f_info->fltr_act == ICE_FWD_TO_Q ||
-            f_info->fltr_act == ICE_FWD_TO_QGRP)) {
-               f_info->lb_en = true;
-               if (!(hw->evb_veb && f_info->lkup_type == ICE_SW_LKUP_MAC &&
-                     is_unicast_ether_addr(f_info->l_data.mac.mac_addr)))
-                       f_info->lan_en = true;
+       fi->lb_en = false;
+       fi->lan_en = false;
+       if ((fi->flag & ICE_FLTR_TX) &&
+           (fi->fltr_act == ICE_FWD_TO_VSI ||
+            fi->fltr_act == ICE_FWD_TO_VSI_LIST ||
+            fi->fltr_act == ICE_FWD_TO_Q ||
+            fi->fltr_act == ICE_FWD_TO_QGRP)) {
+               fi->lb_en = true;
+               /* Do not set lan_en to TRUE if
+                * 1. The switch is a VEB AND
+                * 2
+                * 2.1 The lookup is MAC with unicast addr for MAC, OR
+                * 2.2 The lookup is MAC_VLAN with unicast addr for MAC
+                *
+                * In all other cases, the LAN enable has to be set to true.
+                */
+               if (!(hw->evb_veb &&
+                     ((fi->lkup_type == ICE_SW_LKUP_MAC &&
+                       is_unicast_ether_addr(fi->l_data.mac.mac_addr)) ||
+                      (fi->lkup_type == ICE_SW_LKUP_MAC_VLAN &&
+                       is_unicast_ether_addr(fi->l_data.mac_vlan.mac_addr)))))
+                       fi->lan_en = true;
        }
 }
 
@@ -817,7 +827,7 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,
        /* Create two back-to-back switch rules and submit them to the HW using
         * one memory buffer:
         *    1. Large Action
-        *    2. Look up tx rx
+        *    2. Look up Tx Rx
         */
        lg_act_size = (u16)ICE_SW_RULE_LG_ACT_SIZE(num_lg_acts);
        rules_size = lg_act_size + ICE_SW_RULE_RX_TX_ETH_HDR_SIZE;
@@ -861,7 +871,7 @@ ice_add_marker_act(struct ice_hw *hw, struct ice_fltr_mgmt_list_entry *m_ent,
 
        lg_act->pdata.lg_act.act[2] = cpu_to_le32(act);
 
-       /* call the fill switch rule to fill the lookup tx rx structure */
+       /* call the fill switch rule to fill the lookup Tx Rx structure */
        ice_fill_sw_rule(hw, &m_ent->fltr_info, rx_tx,
                         ice_aqc_opc_update_sw_rules);
 
@@ -1158,8 +1168,8 @@ enum ice_status ice_update_sw_rule_bridge_mode(struct ice_hw *hw)
  * Call AQ command to add or update previously created VSI list with new VSI.
  *
  * Helper function to do book keeping associated with adding filter information
- * The algorithm to do the booking keeping is described below :
- * When a VSI needs to subscribe to a given filterMAC/VLAN/Ethtype etc.)
+ * The algorithm to do the book keeping is described below :
+ * When a VSI needs to subscribe to a given filter (MAC/VLAN/Ethtype etc.)
  *     if only one VSI has been added till now
  *             Allocate a new VSI list and add two VSIs
  *             to this list using switch rule command
@@ -1237,6 +1247,9 @@ ice_add_update_vsi_list(struct ice_hw *hw,
                u16 vsi_handle = new_fltr->vsi_handle;
                enum ice_adminq_opc opcode;
 
+               if (!m_entry->vsi_list_info)
+                       return ICE_ERR_CFG;
+
                /* A rule already exists with the new VSI being added */
                if (test_bit(vsi_handle, m_entry->vsi_list_info->vsi_map))
                        return 0;
@@ -1853,7 +1866,7 @@ ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
                tmp_fltr.fwd_id.vsi_list_id = vsi_list_id;
                tmp_fltr.fltr_act = ICE_FWD_TO_VSI_LIST;
                /* Update the previous switch rule to a new VSI list which
-                * includes current VSI thats requested
+                * includes current VSI that is requested
                 */
                status = ice_update_pkt_fwd_rule(hw, &tmp_fltr);
                if (status)
index fe5bbab..49fc380 100644 (file)
@@ -219,7 +219,7 @@ static bool ice_clean_tx_irq(struct ice_vsi *vsi, struct ice_ring *tx_ring,
 
 /**
  * ice_setup_tx_ring - Allocate the Tx descriptors
- * @tx_ring: the tx ring to set up
+ * @tx_ring: the Tx ring to set up
  *
  * Return 0 on success, negative on error
  */
@@ -324,7 +324,7 @@ void ice_free_rx_ring(struct ice_ring *rx_ring)
 
 /**
  * ice_setup_rx_ring - Allocate the Rx descriptors
- * @rx_ring: the rx ring to set up
+ * @rx_ring: the Rx ring to set up
  *
  * Return 0 on success, negative on error
  */
@@ -377,7 +377,7 @@ static void ice_release_rx_desc(struct ice_ring *rx_ring, u32 val)
        rx_ring->next_to_alloc = val;
 
        /* Force memory writes to complete before letting h/w
-        * know there are new descriptors to fetch.  (Only
+        * know there are new descriptors to fetch. (Only
         * applicable for weak-ordered memory model archs,
         * such as IA-64).
         */
@@ -586,7 +586,7 @@ static bool ice_add_rx_frag(struct ice_rx_buf *rx_buf,
 
 /**
  * ice_reuse_rx_page - page flip buffer and store it back on the ring
- * @rx_ring: rx descriptor ring to store buffers on
+ * @rx_ring: Rx descriptor ring to store buffers on
  * @old_buf: donor buffer to have page reused
  *
  * Synchronizes page for reuse by the adapter
@@ -609,7 +609,7 @@ static void ice_reuse_rx_page(struct ice_ring *rx_ring,
 
 /**
  * ice_fetch_rx_buf - Allocate skb and populate it
- * @rx_ring: rx descriptor ring to transact packets on
+ * @rx_ring: Rx descriptor ring to transact packets on
  * @rx_desc: descriptor containing info written by hardware
  *
  * This function allocates an skb on the fly, and populates it with the page
@@ -686,7 +686,7 @@ static struct sk_buff *ice_fetch_rx_buf(struct ice_ring *rx_ring,
  * ice_pull_tail - ice specific version of skb_pull_tail
  * @skb: pointer to current skb being adjusted
  *
- * This function is an ice specific version of __pskb_pull_tail.  The
+ * This function is an ice 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.
@@ -768,7 +768,7 @@ static bool ice_test_staterr(union ice_32b_rx_flex_desc *rx_desc,
  * @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 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.
@@ -904,7 +904,7 @@ checksum_fail:
 
 /**
  * ice_process_skb_fields - Populate skb header fields from Rx descriptor
- * @rx_ring: rx descriptor ring packet is being transacted on
+ * @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
  * @ptype: the packet type decoded by hardware
@@ -927,7 +927,7 @@ static void ice_process_skb_fields(struct ice_ring *rx_ring,
 
 /**
  * ice_receive_skb - Send a completed packet up the stack
- * @rx_ring: rx ring in play
+ * @rx_ring: Rx ring in play
  * @skb: packet to send up
  * @vlan_tag: vlan tag for packet
  *
@@ -946,11 +946,11 @@ static void ice_receive_skb(struct ice_ring *rx_ring, struct sk_buff *skb,
 
 /**
  * ice_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf
- * @rx_ring: rx descriptor ring to transact packets on
+ * @rx_ring: Rx descriptor ring to transact packets on
  * @budget: Total limit on number of packets to process
  *
  * This function provides a "bounce buffer" approach to Rx interrupt
- * processing.  The advantage to this is that on systems that have
+ * processing. The advantage to this is that on systems that have
  * expensive overhead for IOMMU access this provides a means of avoiding
  * it by maintaining the mapping of the page to the system.
  *
@@ -1103,11 +1103,14 @@ int ice_napi_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* Work is done so exit the polling mode and re-enable the interrupt */
-       napi_complete_done(napi, work_done);
-       if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
-               ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
-       return 0;
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
+                       ice_irq_dynamic_ena(&vsi->back->hw, vsi, q_vector);
+
+       return min(work_done, budget - 1);
 }
 
 /* helper function for building cmd/type/offset */
@@ -1122,7 +1125,7 @@ build_ctob(u64 td_cmd, u64 td_offset, unsigned int size, u64 td_tag)
 }
 
 /**
- * __ice_maybe_stop_tx - 2nd level check for tx stop conditions
+ * __ice_maybe_stop_tx - 2nd level check for Tx stop conditions
  * @tx_ring: the ring to be checked
  * @size: the size buffer we want to assure is available
  *
@@ -1145,7 +1148,7 @@ static int __ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
 }
 
 /**
- * ice_maybe_stop_tx - 1st level check for tx stop conditions
+ * ice_maybe_stop_tx - 1st level check for Tx stop conditions
  * @tx_ring: the ring to be checked
  * @size:    the size buffer we want to assure is available
  *
@@ -1155,6 +1158,7 @@ static int ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
 {
        if (likely(ICE_DESC_UNUSED(tx_ring) >= size))
                return 0;
+
        return __ice_maybe_stop_tx(tx_ring, size);
 }
 
@@ -1552,7 +1556,7 @@ int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
  * Finally, we add one to round up. Because 256 isn't an exact multiple of
  * 3, we'll underestimate near each multiple of 12K. This is actually more
  * accurate as we have 4K - 1 of wiggle room that we can fit into the last
- * segment.  For our purposes this is accurate out to 1M which is orders of
+ * segment. For our purposes this is accurate out to 1M which is orders of
  * magnitude greater than our largest possible GSO size.
  *
  * This would then be implemented as:
@@ -1568,7 +1572,7 @@ static unsigned int ice_txd_use_count(unsigned int size)
 }
 
 /**
- * ice_xmit_desc_count - calculate number of tx descriptors needed
+ * ice_xmit_desc_count - calculate number of Tx descriptors needed
  * @skb: send buffer
  *
  * Returns number of data descriptors needed for this skb.
@@ -1620,7 +1624,7 @@ static bool __ice_chk_linearize(struct sk_buff *skb)
        nr_frags -= ICE_MAX_BUF_TXD - 2;
        frag = &skb_shinfo(skb)->frags[0];
 
-       /* Initialize size to the negative value of gso_size minus 1.  We
+       /* Initialize size to the negative value of gso_size minus 1. We
         * use this as the worst case scenerio in which the frag ahead
         * of us only provides one byte which is why we are limited to 6
         * descriptors for a single transmit as the header and previous
index f4dbc81..0ea4281 100644 (file)
@@ -124,6 +124,8 @@ struct ice_phy_info {
 
 /* Common HW capabilities for SW use */
 struct ice_hw_common_caps {
+       u32 valid_functions;
+
        /* TX/RX queues */
        u16 num_rxq;            /* Number/Total RX queues */
        u16 rxq_first_id;       /* First queue ID for RX queues */
@@ -150,7 +152,7 @@ struct ice_hw_func_caps {
        struct ice_hw_common_caps common_cap;
        u32 num_allocd_vfs;             /* Number of allocated VFs */
        u32 vf_base_id;                 /* Logical ID of the first VF */
-       u32 guaranteed_num_vsi;
+       u32 guar_num_vsi;
 };
 
 /* Device wide capabilities */
index e71065f..05ff4f9 100644 (file)
@@ -156,8 +156,6 @@ static void ice_free_vf_res(struct ice_vf *vf)
        clear_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states);
 }
 
-/***********************enable_vf routines*****************************/
-
 /**
  * ice_dis_vf_mappings
  * @vf: pointer to the VF structure
@@ -215,6 +213,15 @@ void ice_free_vfs(struct ice_pf *pf)
        while (test_and_set_bit(__ICE_VF_DIS, pf->state))
                usleep_range(1000, 2000);
 
+       /* Disable IOV before freeing resources. This lets any VF drivers
+        * running in the host get themselves cleaned up before we yank
+        * the carpet out from underneath their feet.
+        */
+       if (!pci_vfs_assigned(pf->pdev))
+               pci_disable_sriov(pf->pdev);
+       else
+               dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n");
+
        /* Avoid wait time by stopping all VFs at the same time */
        for (i = 0; i < pf->num_alloc_vfs; i++) {
                if (!test_bit(ICE_VF_STATE_ENA, pf->vf[i].vf_states))
@@ -228,15 +235,6 @@ void ice_free_vfs(struct ice_pf *pf)
                clear_bit(ICE_VF_STATE_ENA, pf->vf[i].vf_states);
        }
 
-       /* Disable IOV before freeing resources. This lets any VF drivers
-        * running in the host get themselves cleaned up before we yank
-        * the carpet out from underneath their feet.
-        */
-       if (!pci_vfs_assigned(pf->pdev))
-               pci_disable_sriov(pf->pdev);
-       else
-               dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n");
-
        tmp = pf->num_alloc_vfs;
        pf->num_vf_qps = 0;
        pf->num_alloc_vfs = 0;
@@ -454,7 +452,7 @@ static int ice_alloc_vsi_res(struct ice_vf *vf)
 
        /* Clear this bit after VF initialization since we shouldn't reclaim
         * and reassign interrupts for synchronous or asynchronous VFR events.
-        * We don't want to reconfigure interrupts since AVF driver doesn't
+        * We dont want to reconfigure interrupts since AVF driver doesn't
         * expect vector assignment to be changed unless there is a request for
         * more vectors.
         */
@@ -1105,7 +1103,7 @@ int ice_sriov_configure(struct pci_dev *pdev, int num_vfs)
  * ice_process_vflr_event - Free VF resources via IRQ calls
  * @pf: pointer to the PF structure
  *
- * called from the VLFR IRQ handler to
+ * called from the VFLR IRQ handler to
  * free up VF resources and state variables
  */
 void ice_process_vflr_event(struct ice_pf *pf)
@@ -1764,7 +1762,7 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
                /* copy Tx queue info from VF into VSI */
                vsi->tx_rings[i]->dma = qpi->txq.dma_ring_addr;
                vsi->tx_rings[i]->count = qpi->txq.ring_len;
-               /* copy Rx queue info from VF into vsi */
+               /* copy Rx queue info from VF into VSI */
                vsi->rx_rings[i]->dma = qpi->rxq.dma_ring_addr;
                vsi->rx_rings[i]->count = qpi->rxq.ring_len;
                if (qpi->rxq.databuffer_size > ((16 * 1024) - 128)) {
@@ -1830,7 +1828,7 @@ static bool ice_can_vf_change_mac(struct ice_vf *vf)
  * @msg: pointer to the msg buffer
  * @set: true if mac filters are being set, false otherwise
  *
- * add guest mac address filter
+ * add guest MAC address filter
  */
 static int
 ice_vc_handle_mac_addr_msg(struct ice_vf *vf, u8 *msg, bool set)
@@ -1968,9 +1966,9 @@ static int ice_vc_del_mac_addr_msg(struct ice_vf *vf, u8 *msg)
  * @msg: pointer to the msg buffer
  *
  * VFs get a default number of queues but can use this message to request a
- * different number.  If the request is successful, PF will reset the VF and
+ * different number. If the request is successful, PF will reset the VF and
  * return 0. If unsuccessful, PF will send message informing VF of number of
- * available queue pairs via virtchnl message response to VF.
+ * available queue pairs via virtchnl message response to vf.
  */
 static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
 {
@@ -1991,7 +1989,7 @@ static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
        tx_rx_queue_left = min_t(int, pf->q_left_tx, pf->q_left_rx);
        if (req_queues <= 0) {
                dev_err(&pf->pdev->dev,
-                       "VF %d tried to request %d queues.  Ignoring.\n",
+                       "VF %d tried to request %d queues. Ignoring.\n",
                        vf->vf_id, req_queues);
        } else if (req_queues > ICE_MAX_QS_PER_VF) {
                dev_err(&pf->pdev->dev,
index 10131e0..01470a8 100644 (file)
@@ -70,7 +70,7 @@ struct ice_vf {
        u8 spoofchk;
        u16 num_mac;
        u16 num_vlan;
-       u8 num_req_qs;          /* num of queue pairs requested by VF */
+       u8 num_req_qs;                  /* num of queue pairs requested by VF */
 };
 
 #ifdef CONFIG_PCI_IOV
index 5acf3b7..c576710 100644 (file)
@@ -2113,7 +2113,7 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
 
-       if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
+       if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_FILTER))
                return -EOPNOTSUPP;
 
        if (!(adapter->flags & IGB_FLAG_WOL_SUPPORTED))
index 5df88ad..453ae1d 100644 (file)
@@ -1850,13 +1850,12 @@ static void igb_config_tx_modes(struct igb_adapter *adapter, int queue)
         * configuration' in respect to these parameters.
         */
 
-       netdev_dbg(netdev, "Qav Tx mode: cbs %s, launchtime %s, queue %d \
-                           idleslope %d sendslope %d hiCredit %d \
-                           locredit %d\n",
-                  (ring->cbs_enable) ? "enabled" : "disabled",
-                  (ring->launchtime_enable) ? "enabled" : "disabled", queue,
-                  ring->idleslope, ring->sendslope, ring->hicredit,
-                  ring->locredit);
+       netdev_dbg(netdev, "Qav Tx mode: cbs %s, launchtime %s, queue %d idleslope %d sendslope %d hiCredit %d locredit %d\n",
+                  ring->cbs_enable ? "enabled" : "disabled",
+                  ring->launchtime_enable ? "enabled" : "disabled",
+                  queue,
+                  ring->idleslope, ring->sendslope,
+                  ring->hicredit, ring->locredit);
 }
 
 static int igb_save_txtime_params(struct igb_adapter *adapter, int queue,
@@ -6019,6 +6018,8 @@ static int igb_tx_map(struct igb_ring *tx_ring,
        /* set the timestamp */
        first->time_stamp = jiffies;
 
+       skb_tx_timestamp(skb);
+
        /* 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).
@@ -6147,8 +6148,6 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
        else if (!tso)
                igb_tx_csum(tx_ring, first);
 
-       skb_tx_timestamp(skb);
-
        if (igb_tx_map(tx_ring, first, hdr_len))
                goto cleanup_tx_tstamp;
 
@@ -7753,11 +7752,13 @@ static int igb_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* If not enough Rx work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       igb_ring_irq_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               igb_ring_irq_enable(q_vector);
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
index 2b95dc9..fd3071f 100644 (file)
@@ -277,17 +277,53 @@ static int igb_ptp_adjtime_i210(struct ptp_clock_info *ptp, s64 delta)
        return 0;
 }
 
-static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp,
-                                struct timespec64 *ts)
+static int igb_ptp_gettimex_82576(struct ptp_clock_info *ptp,
+                                 struct timespec64 *ts,
+                                 struct ptp_system_timestamp *sts)
 {
        struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
                                               ptp_caps);
+       struct e1000_hw *hw = &igb->hw;
        unsigned long flags;
+       u32 lo, hi;
        u64 ns;
 
        spin_lock_irqsave(&igb->tmreg_lock, flags);
 
-       ns = timecounter_read(&igb->tc);
+       ptp_read_system_prets(sts);
+       lo = rd32(E1000_SYSTIML);
+       ptp_read_system_postts(sts);
+       hi = rd32(E1000_SYSTIMH);
+
+       ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
+
+       spin_unlock_irqrestore(&igb->tmreg_lock, flags);
+
+       *ts = ns_to_timespec64(ns);
+
+       return 0;
+}
+
+static int igb_ptp_gettimex_82580(struct ptp_clock_info *ptp,
+                                 struct timespec64 *ts,
+                                 struct ptp_system_timestamp *sts)
+{
+       struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
+                                              ptp_caps);
+       struct e1000_hw *hw = &igb->hw;
+       unsigned long flags;
+       u32 lo, hi;
+       u64 ns;
+
+       spin_lock_irqsave(&igb->tmreg_lock, flags);
+
+       ptp_read_system_prets(sts);
+       rd32(E1000_SYSTIMR);
+       ptp_read_system_postts(sts);
+       lo = rd32(E1000_SYSTIML);
+       hi = rd32(E1000_SYSTIMH);
+
+       ns = timecounter_cyc2time(&igb->tc, ((u64)hi << 32) | lo);
 
        spin_unlock_irqrestore(&igb->tmreg_lock, flags);
 
@@ -296,16 +332,22 @@ static int igb_ptp_gettime_82576(struct ptp_clock_info *ptp,
        return 0;
 }
 
-static int igb_ptp_gettime_i210(struct ptp_clock_info *ptp,
-                               struct timespec64 *ts)
+static int igb_ptp_gettimex_i210(struct ptp_clock_info *ptp,
+                                struct timespec64 *ts,
+                                struct ptp_system_timestamp *sts)
 {
        struct igb_adapter *igb = container_of(ptp, struct igb_adapter,
                                               ptp_caps);
+       struct e1000_hw *hw = &igb->hw;
        unsigned long flags;
 
        spin_lock_irqsave(&igb->tmreg_lock, flags);
 
-       igb_ptp_read_i210(igb, ts);
+       ptp_read_system_prets(sts);
+       rd32(E1000_SYSTIMR);
+       ptp_read_system_postts(sts);
+       ts->tv_nsec = rd32(E1000_SYSTIML);
+       ts->tv_sec = rd32(E1000_SYSTIMH);
 
        spin_unlock_irqrestore(&igb->tmreg_lock, flags);
 
@@ -658,9 +700,12 @@ static void igb_ptp_overflow_check(struct work_struct *work)
        struct igb_adapter *igb =
                container_of(work, struct igb_adapter, ptp_overflow_work.work);
        struct timespec64 ts;
+       u64 ns;
 
-       igb->ptp_caps.gettime64(&igb->ptp_caps, &ts);
+       /* Update the timecounter */
+       ns = timecounter_read(&igb->tc);
 
+       ts = ns_to_timespec64(ns);
        pr_debug("igb overflow check at %lld.%09lu\n",
                 (long long) ts.tv_sec, ts.tv_nsec);
 
@@ -1126,7 +1171,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.pps = 0;
                adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82576;
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
-               adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576;
+               adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82576;
                adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
                adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82576;
@@ -1145,7 +1190,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.pps = 0;
                adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
-               adapter->ptp_caps.gettime64 = igb_ptp_gettime_82576;
+               adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_82580;
                adapter->ptp_caps.settime64 = igb_ptp_settime_82576;
                adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82580;
@@ -1173,7 +1218,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.pin_config = adapter->sdp_config;
                adapter->ptp_caps.adjfine = igb_ptp_adjfine_82580;
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
-               adapter->ptp_caps.gettime64 = igb_ptp_gettime_i210;
+               adapter->ptp_caps.gettimex64 = igb_ptp_gettimex_i210;
                adapter->ptp_caps.settime64 = igb_ptp_settime_i210;
                adapter->ptp_caps.enable = igb_ptp_feature_enable_i210;
                adapter->ptp_caps.verify = igb_ptp_verify_pin;
index 163e583..a3cd7ac 100644 (file)
@@ -241,7 +241,7 @@ static s32 e1000_write_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size)
        s32 err;
        u16 i;
 
-       WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock));
+       lockdep_assert_held(&hw->mbx_lock);
 
        /* lock the mailbox to prevent pf/vf race condition */
        err = e1000_obtain_mbx_lock_vf(hw);
@@ -279,7 +279,7 @@ static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size)
        s32 err;
        u16 i;
 
-       WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock));
+       lockdep_assert_held(&hw->mbx_lock);
 
        /* lock the mailbox to prevent pf/vf race condition */
        err = e1000_obtain_mbx_lock_vf(hw);
index 820d49e..4eab83f 100644 (file)
@@ -1186,10 +1186,13 @@ static int igbvf_poll(struct napi_struct *napi, int budget)
 
        igbvf_clean_rx_irq(adapter, &work_done, budget);
 
-       /* If not enough Rx work done, exit the polling mode */
-       if (work_done < budget) {
-               napi_complete_done(napi, work_done);
+       if (work_done == budget)
+               return budget;
 
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
                if (adapter->requested_itr & 3)
                        igbvf_set_itr(adapter);
 
index cdf18a5..b1039dd 100644 (file)
@@ -5,23 +5,12 @@
 #define _IGC_H_
 
 #include <linux/kobject.h>
-
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
-
 #include <linux/ethtool.h>
-
 #include <linux/sctp.h>
 
-#define IGC_ERR(args...) pr_err("igc: " args)
-
-#define PFX "igc: "
-
-#include <linux/timecounter.h>
-#include <linux/net_tstamp.h>
-#include <linux/ptp_clock_kernel.h>
-
 #include "igc_hw.h"
 
 /* main */
index 832da60..df40af7 100644 (file)
@@ -237,7 +237,6 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)
 {
        struct igc_phy_info *phy = &hw->phy;
        s32 ret_val = 0;
-       u32 ctrl_ext;
 
        if (hw->phy.media_type != igc_media_type_copper) {
                phy->type = igc_phy_none;
@@ -247,8 +246,6 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)
        phy->autoneg_mask       = AUTONEG_ADVERTISE_SPEED_DEFAULT_2500;
        phy->reset_delay_us     = 100;
 
-       ctrl_ext = rd32(IGC_CTRL_EXT);
-
        /* set lan id */
        hw->bus.func = (rd32(IGC_STATUS) & IGC_STATUS_FUNC_MASK) >>
                        IGC_STATUS_FUNC_SHIFT;
@@ -287,8 +284,6 @@ out:
 static s32 igc_get_invariants_base(struct igc_hw *hw)
 {
        struct igc_mac_info *mac = &hw->mac;
-       u32 link_mode = 0;
-       u32 ctrl_ext = 0;
        s32 ret_val = 0;
 
        switch (hw->device_id) {
@@ -302,9 +297,6 @@ static s32 igc_get_invariants_base(struct igc_hw *hw)
 
        hw->phy.media_type = igc_media_type_copper;
 
-       ctrl_ext = rd32(IGC_CTRL_EXT);
-       link_mode = ctrl_ext & IGC_CTRL_EXT_LINK_MODE_MASK;
-
        /* mac initialization and operations */
        ret_val = igc_init_mac_params_base(hw);
        if (ret_val)
index 9d85707..f201830 100644 (file)
@@ -865,6 +865,8 @@ static int igc_tx_map(struct igc_ring *tx_ring,
        /* set the timestamp */
        first->time_stamp = jiffies;
 
+       skb_tx_timestamp(skb);
+
        /* 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).
@@ -959,8 +961,6 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
        first->bytecount = skb->len;
        first->gso_segs = 1;
 
-       skb_tx_timestamp(skb);
-
        /* record initial flags and protocol */
        first->tx_flags = tx_flags;
        first->protocol = protocol;
@@ -1108,7 +1108,7 @@ static struct sk_buff *igc_build_skb(struct igc_ring *rx_ring,
 
        /* update pointers within the skb to store the data */
        skb_reserve(skb, IGC_SKB_PAD);
-        __skb_put(skb, size);
+       __skb_put(skb, size);
 
        /* update buffer offset */
 #if (PAGE_SIZE < 8192)
@@ -1160,9 +1160,9 @@ static struct sk_buff *igc_construct_skb(struct igc_ring *rx_ring,
                                (va + headlen) - page_address(rx_buffer->page),
                                size, truesize);
 #if (PAGE_SIZE < 8192)
-       rx_buffer->page_offset ^= truesize;
+               rx_buffer->page_offset ^= truesize;
 #else
-       rx_buffer->page_offset += truesize;
+               rx_buffer->page_offset += truesize;
 #endif
        } else {
                rx_buffer->pagecnt_bias++;
@@ -1668,8 +1668,8 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
                                tx_buffer->next_to_watch,
                                jiffies,
                                tx_buffer->next_to_watch->wb.status);
-                               netif_stop_subqueue(tx_ring->netdev,
-                                                   tx_ring->queue_index);
+                       netif_stop_subqueue(tx_ring->netdev,
+                                           tx_ring->queue_index);
 
                        /* we are about to reset, no point in enabling stuff */
                        return true;
@@ -1700,20 +1700,6 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
 }
 
 /**
- * igc_ioctl - I/O control method
- * @netdev: network interface device structure
- * @ifreq: frequency
- * @cmd: command
- */
-static int igc_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
-{
-       switch (cmd) {
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-/**
  * igc_up - Open the interface and prepare it to handle traffic
  * @adapter: board private structure
  */
@@ -2866,11 +2852,13 @@ static int igc_poll(struct napi_struct *napi, int budget)
        if (!clean_complete)
                return budget;
 
-       /* If not enough Rx work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       igc_ring_irq_enable(q_vector);
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done)))
+               igc_ring_irq_enable(q_vector);
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
@@ -3358,7 +3346,7 @@ static int __igc_open(struct net_device *netdev, bool resuming)
                goto err_req_irq;
 
        /* Notify the stack of the actual queue counts. */
-       netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
+       err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
        if (err)
                goto err_set_queues;
 
@@ -3445,7 +3433,6 @@ static const struct net_device_ops igc_netdev_ops = {
        .ndo_set_mac_address    = igc_set_mac,
        .ndo_change_mtu         = igc_change_mtu,
        .ndo_get_stats          = igc_get_stats,
-       .ndo_do_ioctl           = igc_ioctl,
 };
 
 /* PCIe configuration access */
@@ -3532,26 +3519,23 @@ static int igc_probe(struct pci_dev *pdev,
        struct net_device *netdev;
        struct igc_hw *hw;
        const struct igc_info *ei = igc_info_tbl[ent->driver_data];
-       int err, pci_using_dac;
+       int err;
 
        err = pci_enable_device_mem(pdev);
        if (err)
                return err;
 
-       pci_using_dac = 0;
        err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
        if (!err) {
                err = dma_set_coherent_mask(&pdev->dev,
                                            DMA_BIT_MASK(64));
-               if (!err)
-                       pci_using_dac = 1;
        } else {
                err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
                if (err) {
                        err = dma_set_coherent_mask(&pdev->dev,
                                                    DMA_BIT_MASK(32));
                        if (err) {
-                               IGC_ERR("Wrong DMA configuration, aborting\n");
+                               dev_err(&pdev->dev, "igc: Wrong DMA config\n");
                                goto err_dma;
                        }
                }
index 732b1e6..acba067 100644 (file)
@@ -2206,7 +2206,8 @@ static int ixgbe_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
-       if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
+       if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE |
+                           WAKE_FILTER))
                return -EOPNOTSUPP;
 
        if (ixgbe_wol_exclusion(adapter, wol))
index fd1b054..4d77f42 100644 (file)
@@ -4,6 +4,7 @@
 #include "ixgbe.h"
 #include <net/xfrm.h>
 #include <crypto/aead.h>
+#include <linux/if_bridge.h>
 
 #define IXGBE_IPSEC_KEY_BITS  160
 static const char aes_gcm_name[] = "rfc4106(gcm(aes))";
@@ -693,7 +694,8 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs)
        } else {
                struct tx_sa tsa;
 
-               if (adapter->num_vfs)
+               if (adapter->num_vfs &&
+                   adapter->bridge_mode != BRIDGE_MODE_VEPA)
                        return -EOPNOTSUPP;
 
                /* find the first unused index */
index 113b38e..49a4ea3 100644 (file)
@@ -6077,9 +6077,9 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
        /* Disable Rx */
        ixgbe_disable_rx(adapter);
 
-       /* synchronize_sched() needed for pending XDP buffers to drain */
+       /* synchronize_rcu() needed for pending XDP buffers to drain */
        if (adapter->xdp_ring[0])
-               synchronize_sched();
+               synchronize_rcu();
 
        ixgbe_irq_disable(adapter);
 
@@ -8269,6 +8269,8 @@ static int ixgbe_tx_map(struct ixgbe_ring *tx_ring,
        /* set the timestamp */
        first->time_stamp = jiffies;
 
+       skb_tx_timestamp(skb);
+
        /*
         * Force memory writes to complete before letting h/w know there
         * are new descriptors to fetch.  (Only applicable for weak-ordered
@@ -8646,8 +8648,6 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
                }
        }
 
-       skb_tx_timestamp(skb);
-
 #ifdef CONFIG_PCI_IOV
        /*
         * Use the l2switch_enable flag - would be false if the DMA
@@ -10476,7 +10476,7 @@ void ixgbe_txrx_ring_disable(struct ixgbe_adapter *adapter, int ring)
        ixgbe_disable_rxr_hw(adapter, rx_ring);
 
        if (xdp_ring)
-               synchronize_sched();
+               synchronize_rcu();
 
        /* Rx/Tx/XDP Tx share the same napi context. */
        napi_disable(&rx_ring->q_vector->napi);
@@ -10517,7 +10517,8 @@ void ixgbe_txrx_ring_enable(struct ixgbe_adapter *adapter, int ring)
        ixgbe_configure_rx_ring(adapter, rx_ring);
 
        clear_bit(__IXGBE_TX_DISABLED, &tx_ring->state);
-       clear_bit(__IXGBE_TX_DISABLED, &xdp_ring->state);
+       if (xdp_ring)
+               clear_bit(__IXGBE_TX_DISABLED, &xdp_ring->state);
 }
 
 /**
index b3e0d8b..d81a50d 100644 (file)
@@ -443,22 +443,52 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 }
 
 /**
- * ixgbe_ptp_gettime
+ * ixgbe_ptp_gettimex
  * @ptp: the ptp clock structure
- * @ts: timespec structure to hold the current time value
+ * @ts: timespec to hold the PHC timestamp
+ * @sts: structure to hold the system time before and after reading the PHC
  *
  * read the timecounter and return the correct value on ns,
  * after converting it into a struct timespec.
  */
-static int ixgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int ixgbe_ptp_gettimex(struct ptp_clock_info *ptp,
+                             struct timespec64 *ts,
+                             struct ptp_system_timestamp *sts)
 {
        struct ixgbe_adapter *adapter =
                container_of(ptp, struct ixgbe_adapter, ptp_caps);
+       struct ixgbe_hw *hw = &adapter->hw;
        unsigned long flags;
-       u64 ns;
+       u64 ns, stamp;
 
        spin_lock_irqsave(&adapter->tmreg_lock, flags);
-       ns = timecounter_read(&adapter->hw_tc);
+
+       switch (adapter->hw.mac.type) {
+       case ixgbe_mac_X550:
+       case ixgbe_mac_X550EM_x:
+       case ixgbe_mac_x550em_a:
+               /* Upper 32 bits represent billions of cycles, lower 32 bits
+                * represent cycles. However, we use timespec64_to_ns for the
+                * correct math even though the units haven't been corrected
+                * yet.
+                */
+               ptp_read_system_prets(sts);
+               IXGBE_READ_REG(hw, IXGBE_SYSTIMR);
+               ptp_read_system_postts(sts);
+               ts->tv_nsec = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+               ts->tv_sec = IXGBE_READ_REG(hw, IXGBE_SYSTIMH);
+               stamp = timespec64_to_ns(ts);
+               break;
+       default:
+               ptp_read_system_prets(sts);
+               stamp = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+               ptp_read_system_postts(sts);
+               stamp |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
+               break;
+       }
+
+       ns = timecounter_cyc2time(&adapter->hw_tc, stamp);
+
        spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
 
        *ts = ns_to_timespec64(ns);
@@ -567,10 +597,14 @@ void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter)
 {
        bool timeout = time_is_before_jiffies(adapter->last_overflow_check +
                                             IXGBE_OVERFLOW_PERIOD);
-       struct timespec64 ts;
+       unsigned long flags;
 
        if (timeout) {
-               ixgbe_ptp_gettime(&adapter->ptp_caps, &ts);
+               /* Update the timecounter */
+               spin_lock_irqsave(&adapter->tmreg_lock, flags);
+               timecounter_read(&adapter->hw_tc);
+               spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
+
                adapter->last_overflow_check = jiffies;
        }
 }
@@ -1216,7 +1250,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.pps = 1;
                adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
-               adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
+               adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
                adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
                adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540;
@@ -1233,7 +1267,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.pps = 0;
                adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
-               adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
+               adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
                adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
                adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                break;
@@ -1249,7 +1283,7 @@ static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.pps = 0;
                adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550;
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
-               adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
+               adapter->ptp_caps.gettimex64 = ixgbe_ptp_gettimex;
                adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
                adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                adapter->ptp_setup_sdp = NULL;
index 5e47ede..2de81f0 100644 (file)
@@ -1293,16 +1293,20 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
        /* If all work not completed, return budget and keep polling */
        if (!clean_complete)
                return budget;
-       /* all work done, exit the polling mode */
-       napi_complete_done(napi, work_done);
-       if (adapter->rx_itr_setting == 1)
-               ixgbevf_set_itr(q_vector);
-       if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
-           !test_bit(__IXGBEVF_REMOVING, &adapter->state))
-               ixgbevf_irq_enable_queues(adapter,
-                                         BIT(q_vector->v_idx));
 
-       return 0;
+       /* Exit the polling mode, but don't re-enable interrupts if stack might
+        * poll us due to busy-polling
+        */
+       if (likely(napi_complete_done(napi, work_done))) {
+               if (adapter->rx_itr_setting == 1)
+                       ixgbevf_set_itr(q_vector);
+               if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
+                   !test_bit(__IXGBEVF_REMOVING, &adapter->state))
+                       ixgbevf_irq_enable_queues(adapter,
+                                                 BIT(q_vector->v_idx));
+       }
+
+       return min(work_done, budget - 1);
 }
 
 /**
@@ -4016,6 +4020,8 @@ static void ixgbevf_tx_map(struct ixgbevf_ring *tx_ring,
        /* set the timestamp */
        first->time_stamp = jiffies;
 
+       skb_tx_timestamp(skb);
+
        /* 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).
index 1e9bcbd..2f42727 100644 (file)
@@ -1499,23 +1499,16 @@ mv643xx_eth_get_link_ksettings_phy(struct mv643xx_eth_private *mp,
                                   struct ethtool_link_ksettings *cmd)
 {
        struct net_device *dev = mp->dev;
-       u32 supported, advertising;
 
        phy_ethtool_ksettings_get(dev->phydev, cmd);
 
        /*
         * The MAC does not support 1000baseT_Half.
         */
-       ethtool_convert_link_mode_to_legacy_u32(&supported,
-                                               cmd->link_modes.supported);
-       ethtool_convert_link_mode_to_legacy_u32(&advertising,
-                                               cmd->link_modes.advertising);
-       supported &= ~SUPPORTED_1000baseT_Half;
-       advertising &= ~ADVERTISED_1000baseT_Half;
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
-                                               supported);
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
-                                               advertising);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                          cmd->link_modes.supported);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                          cmd->link_modes.advertising);
 
        return 0;
 }
@@ -3031,10 +3024,12 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
                phy->autoneg = AUTONEG_ENABLE;
                phy->speed = 0;
                phy->duplex = 0;
-               phy->advertising = phy->supported | ADVERTISED_Autoneg;
+               linkmode_copy(phy->advertising, phy->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                phy->advertising);
        } else {
                phy->autoneg = AUTONEG_DISABLE;
-               phy->advertising = 0;
+               linkmode_zero(phy->advertising);
                phy->speed = speed;
                phy->duplex = duplex;
        }
index e5397c8..46a0f6b 100644 (file)
@@ -4248,8 +4248,7 @@ static int mvneta_ethtool_set_eee(struct net_device *dev,
 
        /* The Armada 37x documents do not give limits for this other than
         * it being an 8-bit register. */
-       if (eee->tx_lpi_enabled &&
-           (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
+       if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255)
                return -EINVAL;
 
        lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
index 12db256..742f0c1 100644 (file)
@@ -31,6 +31,7 @@
  * @resp:              command response
  * @link_info:         link related information
  * @event_cb:          callback for linkchange events
+ * @event_cb_lock:     lock for serializing callback with unregister
  * @cmd_pend:          flag set before new command is started
  *                     flag cleared after command response is received
  * @cgx:               parent cgx port
@@ -43,6 +44,7 @@ struct lmac {
        u64 resp;
        struct cgx_link_user_info link_info;
        struct cgx_event_cb event_cb;
+       spinlock_t event_cb_lock;
        bool cmd_pend;
        struct cgx *cgx;
        u8 lmac_id;
@@ -55,6 +57,8 @@ struct cgx {
        u8                      cgx_id;
        u8                      lmac_count;
        struct lmac             *lmac_idmap[MAX_LMAC_PER_CGX];
+       struct                  work_struct cgx_cmd_work;
+       struct                  workqueue_struct *cgx_cmd_workq;
        struct list_head        cgx_list;
 };
 
@@ -66,6 +70,9 @@ static u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX];
 /* Convert firmware lmac type encoding to string */
 static char *cgx_lmactype_string[LMAC_MODE_MAX];
 
+/* CGX PHY management internal APIs */
+static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en);
+
 /* Supported devices */
 static const struct pci_device_id cgx_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },
@@ -92,17 +99,21 @@ static inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx)
        return cgx->lmac_idmap[lmac_id];
 }
 
-int cgx_get_cgx_cnt(void)
+int cgx_get_cgxcnt_max(void)
 {
        struct cgx *cgx_dev;
-       int count = 0;
+       int idmax = -ENODEV;
 
        list_for_each_entry(cgx_dev, &cgx_list, cgx_list)
-               count++;
+               if (cgx_dev->cgx_id > idmax)
+                       idmax = cgx_dev->cgx_id;
+
+       if (idmax < 0)
+               return 0;
 
-       return count;
+       return idmax + 1;
 }
-EXPORT_SYMBOL(cgx_get_cgx_cnt);
+EXPORT_SYMBOL(cgx_get_cgxcnt_max);
 
 int cgx_get_lmac_cnt(void *cgxd)
 {
@@ -445,6 +456,9 @@ static inline void cgx_link_change_handler(u64 lstat,
        lmac->link_info = event.link_uinfo;
        linfo = &lmac->link_info;
 
+       /* Ensure callback doesn't get unregistered until we finish it */
+       spin_lock(&lmac->event_cb_lock);
+
        if (!lmac->event_cb.notify_link_chg) {
                dev_dbg(dev, "cgx port %d:%d Link change handler null",
                        cgx->cgx_id, lmac->lmac_id);
@@ -455,11 +469,13 @@ static inline void cgx_link_change_handler(u64 lstat,
                dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n",
                         cgx->cgx_id, lmac->lmac_id,
                         linfo->link_up ? "UP" : "DOWN", linfo->speed);
-               return;
+               goto err;
        }
 
        if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data))
                dev_err(dev, "event notification failure\n");
+err:
+       spin_unlock(&lmac->event_cb_lock);
 }
 
 static inline bool cgx_cmdresp_is_linkevent(u64 event)
@@ -482,6 +498,60 @@ static inline bool cgx_event_is_linkevent(u64 event)
                return false;
 }
 
+static inline int cgx_fwi_get_mkex_prfl_sz(u64 *prfl_sz,
+                                          struct cgx *cgx)
+{
+       u64 req = 0;
+       u64 resp;
+       int err;
+
+       req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_SIZE, req);
+       err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
+       if (!err)
+               *prfl_sz = FIELD_GET(RESP_MKEX_PRFL_SIZE, resp);
+
+       return err;
+}
+
+static inline int cgx_fwi_get_mkex_prfl_addr(u64 *prfl_addr,
+                                            struct cgx *cgx)
+{
+       u64 req = 0;
+       u64 resp;
+       int err;
+
+       req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_ADDR, req);
+       err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
+       if (!err)
+               *prfl_addr = FIELD_GET(RESP_MKEX_PRFL_ADDR, resp);
+
+       return err;
+}
+
+int cgx_get_mkex_prfl_info(u64 *addr, u64 *size)
+{
+       struct cgx *cgx_dev;
+       int err;
+
+       if (!addr || !size)
+               return -EINVAL;
+
+       cgx_dev = list_first_entry(&cgx_list, struct cgx, cgx_list);
+       if (!cgx_dev)
+               return -ENXIO;
+
+       err = cgx_fwi_get_mkex_prfl_sz(size, cgx_dev);
+       if (err)
+               return -EIO;
+
+       err = cgx_fwi_get_mkex_prfl_addr(addr, cgx_dev);
+       if (err)
+               return -EIO;
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_get_mkex_prfl_info);
+
 static irqreturn_t cgx_fwi_event_handler(int irq, void *data)
 {
        struct lmac *lmac = data;
@@ -548,6 +618,38 @@ int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id)
 }
 EXPORT_SYMBOL(cgx_lmac_evh_register);
 
+int cgx_lmac_evh_unregister(void *cgxd, int lmac_id)
+{
+       struct lmac *lmac;
+       unsigned long flags;
+       struct cgx *cgx = cgxd;
+
+       lmac = lmac_pdata(lmac_id, cgx);
+       if (!lmac)
+               return -ENODEV;
+
+       spin_lock_irqsave(&lmac->event_cb_lock, flags);
+       lmac->event_cb.notify_link_chg = NULL;
+       lmac->event_cb.data = NULL;
+       spin_unlock_irqrestore(&lmac->event_cb_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_lmac_evh_unregister);
+
+static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable)
+{
+       u64 req = 0;
+       u64 resp;
+
+       if (enable)
+               req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req);
+       else
+               req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req);
+
+       return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
+}
+
 static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx)
 {
        u64 req = 0;
@@ -581,6 +683,34 @@ static int cgx_lmac_verify_fwi_version(struct cgx *cgx)
                return 0;
 }
 
+static void cgx_lmac_linkup_work(struct work_struct *work)
+{
+       struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work);
+       struct device *dev = &cgx->pdev->dev;
+       int i, err;
+
+       /* Do Link up for all the lmacs */
+       for (i = 0; i < cgx->lmac_count; i++) {
+               err = cgx_fwi_link_change(cgx, i, true);
+               if (err)
+                       dev_info(dev, "cgx port %d:%d Link up command failed\n",
+                                cgx->cgx_id, i);
+       }
+}
+
+int cgx_lmac_linkup_start(void *cgxd)
+{
+       struct cgx *cgx = cgxd;
+
+       if (!cgx)
+               return -ENODEV;
+
+       queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(cgx_lmac_linkup_start);
+
 static int cgx_lmac_init(struct cgx *cgx)
 {
        struct lmac *lmac;
@@ -602,6 +732,7 @@ static int cgx_lmac_init(struct cgx *cgx)
                lmac->cgx = cgx;
                init_waitqueue_head(&lmac->wq_cmd_cmplt);
                mutex_init(&lmac->cmd_lock);
+               spin_lock_init(&lmac->event_cb_lock);
                err = request_irq(pci_irq_vector(cgx->pdev,
                                                 CGX_LMAC_FWI + i * 9),
                                   cgx_fwi_event_handler, 0, lmac->name, lmac);
@@ -624,6 +755,12 @@ static int cgx_lmac_exit(struct cgx *cgx)
        struct lmac *lmac;
        int i;
 
+       if (cgx->cgx_cmd_workq) {
+               flush_workqueue(cgx->cgx_cmd_workq);
+               destroy_workqueue(cgx->cgx_cmd_workq);
+               cgx->cgx_cmd_workq = NULL;
+       }
+
        /* Free all lmac related resources */
        for (i = 0; i < cgx->lmac_count; i++) {
                lmac = cgx->lmac_idmap[i];
@@ -679,8 +816,19 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_regions;
        }
 
+       cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24)
+               & CGX_ID_MASK;
+
+       /* init wq for processing linkup requests */
+       INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work);
+       cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0);
+       if (!cgx->cgx_cmd_workq) {
+               dev_err(dev, "alloc workqueue failed for cgx cmd");
+               err = -ENOMEM;
+               goto err_release_regions;
+       }
+
        list_add(&cgx->cgx_list, &cgx_list);
-       cgx->cgx_id = cgx_get_cgx_cnt() - 1;
 
        cgx_link_usertable_init();
 
index 0a66d27..206dc5d 100644 (file)
 /* PCI BAR nos */
 #define PCI_CFG_REG_BAR_NUM            0
 
-#define MAX_CGX                                3
+#define CGX_ID_MASK                    0x7
 #define MAX_LMAC_PER_CGX               4
+#define CGX_FIFO_LEN                   65536 /* 64K for both Rx & Tx */
 #define CGX_OFFSET(x)                  ((x) * MAX_LMAC_PER_CGX)
 
 /* Registers */
 #define CGXX_CMRX_CFG                  0x00
-#define  CMR_EN                                        BIT_ULL(55)
-#define  DATA_PKT_TX_EN                                BIT_ULL(53)
-#define  DATA_PKT_RX_EN                                BIT_ULL(54)
-#define  CGX_LMAC_TYPE_SHIFT                   40
-#define  CGX_LMAC_TYPE_MASK                    0xF
+#define CMR_EN                         BIT_ULL(55)
+#define DATA_PKT_TX_EN                 BIT_ULL(53)
+#define DATA_PKT_RX_EN                 BIT_ULL(54)
+#define CGX_LMAC_TYPE_SHIFT            40
+#define CGX_LMAC_TYPE_MASK             0xF
 #define CGXX_CMRX_INT                  0x040
-#define  FW_CGX_INT                            BIT_ULL(1)
+#define FW_CGX_INT                     BIT_ULL(1)
 #define CGXX_CMRX_INT_ENA_W1S          0x058
 #define CGXX_CMRX_RX_ID_MAP            0x060
 #define CGXX_CMRX_RX_STAT0             0x070
 #define CGXX_CMRX_RX_LMACS             0x128
 #define CGXX_CMRX_RX_DMAC_CTL0         0x1F8
-#define  CGX_DMAC_CTL0_CAM_ENABLE              BIT_ULL(3)
-#define  CGX_DMAC_CAM_ACCEPT                   BIT_ULL(3)
-#define  CGX_DMAC_MCAST_MODE                   BIT_ULL(1)
-#define  CGX_DMAC_BCAST_MODE                   BIT_ULL(0)
+#define CGX_DMAC_CTL0_CAM_ENABLE       BIT_ULL(3)
+#define CGX_DMAC_CAM_ACCEPT            BIT_ULL(3)
+#define CGX_DMAC_MCAST_MODE            BIT_ULL(1)
+#define CGX_DMAC_BCAST_MODE            BIT_ULL(0)
 #define CGXX_CMRX_RX_DMAC_CAM0         0x200
-#define  CGX_DMAC_CAM_ADDR_ENABLE              BIT_ULL(48)
+#define CGX_DMAC_CAM_ADDR_ENABLE       BIT_ULL(48)
 #define CGXX_CMRX_RX_DMAC_CAM1         0x400
-#define CGX_RX_DMAC_ADR_MASK                   GENMASK_ULL(47, 0)
+#define CGX_RX_DMAC_ADR_MASK           GENMASK_ULL(47, 0)
 #define CGXX_CMRX_TX_STAT0             0x700
 #define CGXX_SCRATCH0_REG              0x1050
 #define CGXX_SCRATCH1_REG              0x1058
 #define CGX_CONST                      0x2000
 #define CGXX_SPUX_CONTROL1             0x10000
-#define  CGXX_SPUX_CONTROL1_LBK                        BIT_ULL(14)
+#define CGXX_SPUX_CONTROL1_LBK         BIT_ULL(14)
 #define CGXX_GMP_PCS_MRX_CTL           0x30000
-#define  CGXX_GMP_PCS_MRX_CTL_LBK              BIT_ULL(14)
+#define CGXX_GMP_PCS_MRX_CTL_LBK       BIT_ULL(14)
 
 #define CGX_COMMAND_REG                        CGXX_SCRATCH1_REG
 #define CGX_EVENT_REG                  CGXX_SCRATCH0_REG
@@ -94,11 +95,12 @@ struct cgx_event_cb {
 
 extern struct pci_driver cgx_driver;
 
-int cgx_get_cgx_cnt(void);
+int cgx_get_cgxcnt_max(void);
 int cgx_get_lmac_cnt(void *cgxd);
 void *cgx_get_pdata(int cgx_id);
 int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind);
 int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id);
+int cgx_lmac_evh_unregister(void *cgxd, int lmac_id);
 int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat);
 int cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat);
 int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable);
@@ -108,4 +110,6 @@ void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable);
 int cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable);
 int cgx_get_link_info(void *cgxd, int lmac_id,
                      struct cgx_link_user_info *linfo);
+int cgx_lmac_linkup_start(void *cgxd);
+int cgx_get_mkex_prfl_info(u64 *addr, u64 *size);
 #endif /* CGX_H */
index fa17af3..fb3ba49 100644 (file)
@@ -78,8 +78,8 @@ enum cgx_cmd_id {
        CGX_CMD_LINK_STATE_CHANGE,
        CGX_CMD_MODE_CHANGE,            /* hot plug support */
        CGX_CMD_INTF_SHUTDOWN,
-       CGX_CMD_IRQ_ENABLE,
-       CGX_CMD_IRQ_DISABLE,
+       CGX_CMD_GET_MKEX_PRFL_SIZE,
+       CGX_CMD_GET_MKEX_PRFL_ADDR
 };
 
 /* async event ids */
@@ -139,6 +139,16 @@ enum cgx_cmd_own {
  */
 #define RESP_MAC_ADDR          GENMASK_ULL(56, 9)
 
+/* Response to cmd ID as CGX_CMD_GET_MKEX_PRFL_SIZE with cmd status as
+ * CGX_STAT_SUCCESS
+ */
+#define RESP_MKEX_PRFL_SIZE            GENMASK_ULL(63, 9)
+
+/* Response to cmd ID as CGX_CMD_GET_MKEX_PRFL_ADDR with cmd status as
+ * CGX_STAT_SUCCESS
+ */
+#define RESP_MKEX_PRFL_ADDR            GENMASK_ULL(63, 9)
+
 /* Response to cmd ID - CGX_CMD_LINK_BRING_UP/DOWN, event ID CGX_EVT_LINK_CHANGE
  * status can be either CGX_STAT_FAIL or CGX_STAT_SUCCESS
  *
index d39ada4..ec50a21 100644 (file)
@@ -143,6 +143,14 @@ enum nix_scheduler {
        NIX_TXSCH_LVL_CNT = 0x5,
 };
 
+#define TXSCH_TL1_DFLT_RR_QTM      ((1 << 24) - 1)
+#define TXSCH_TL1_DFLT_RR_PRIO     (0x1ull)
+
+/* Min/Max packet sizes, excluding FCS */
+#define        NIC_HW_MIN_FRS                  40
+#define        NIC_HW_MAX_FRS                  9212
+#define        SDP_HW_MAX_FRS                  65535
+
 /* NIX RX action operation*/
 #define NIX_RX_ACTIONOP_DROP           (0x0ull)
 #define NIX_RX_ACTIONOP_UCAST          (0x1ull)
@@ -169,7 +177,9 @@ enum nix_scheduler {
 
 #define MAX_LMAC_PKIND                 12
 #define NIX_LINK_CGX_LMAC(a, b)                (0 + 4 * (a) + (b))
+#define NIX_LINK_LBK(a)                        (12 + (a))
 #define NIX_CHAN_CGX_LMAC_CHX(a, b, c) (0x800 + 0x100 * (a) + 0x10 * (b) + (c))
+#define NIX_CHAN_LBK_CHX(a, b)         (0 + 0x100 * (a) + (b))
 
 /* NIX LSO format indices.
  * As of now TSO is the only one using, so statically assigning indices.
@@ -186,26 +196,4 @@ enum nix_scheduler {
 #define DEFAULT_RSS_CONTEXT_GROUP      0
 #define MAX_RSS_INDIR_TBL_SIZE         256 /* 1 << Max adder bits */
 
-/* NIX flow tag, key type flags */
-#define FLOW_KEY_TYPE_PORT     BIT(0)
-#define FLOW_KEY_TYPE_IPV4     BIT(1)
-#define FLOW_KEY_TYPE_IPV6     BIT(2)
-#define FLOW_KEY_TYPE_TCP      BIT(3)
-#define FLOW_KEY_TYPE_UDP      BIT(4)
-#define FLOW_KEY_TYPE_SCTP     BIT(5)
-
-/* NIX flow tag algorithm indices, max is 31 */
-enum {
-       FLOW_KEY_ALG_PORT,
-       FLOW_KEY_ALG_IP,
-       FLOW_KEY_ALG_TCP,
-       FLOW_KEY_ALG_UDP,
-       FLOW_KEY_ALG_SCTP,
-       FLOW_KEY_ALG_TCP_UDP,
-       FLOW_KEY_ALG_TCP_SCTP,
-       FLOW_KEY_ALG_UDP_SCTP,
-       FLOW_KEY_ALG_TCP_UDP_SCTP,
-       FLOW_KEY_ALG_MAX,
-};
-
 #endif /* COMMON_H */
index 85ba24a..d6f9ed8 100644 (file)
@@ -290,7 +290,7 @@ EXPORT_SYMBOL(otx2_mbox_nonempty);
 const char *otx2_mbox_id2name(u16 id)
 {
        switch (id) {
-#define M(_name, _id, _1, _2) case _id: return # _name;
+#define M(_name, _id, _1, _2, _3) case _id: return # _name;
        MBOX_MESSAGES
 #undef M
        default:
index a15a59c..76a4575 100644 (file)
@@ -120,54 +120,101 @@ static inline struct mbox_msghdr *otx2_mbox_alloc_msg(struct otx2_mbox *mbox,
 
 #define MBOX_MESSAGES                                                  \
 /* Generic mbox IDs (range 0x000 - 0x1FF) */                           \
-M(READY,               0x001, msg_req, ready_msg_rsp)                  \
-M(ATTACH_RESOURCES,    0x002, rsrc_attach, msg_rsp)                    \
-M(DETACH_RESOURCES,    0x003, rsrc_detach, msg_rsp)                    \
-M(MSIX_OFFSET,         0x004, msg_req, msix_offset_rsp)                \
+M(READY,               0x001, ready, msg_req, ready_msg_rsp)           \
+M(ATTACH_RESOURCES,    0x002, attach_resources, rsrc_attach, msg_rsp)  \
+M(DETACH_RESOURCES,    0x003, detach_resources, rsrc_detach, msg_rsp)  \
+M(MSIX_OFFSET,         0x004, msix_offset, msg_req, msix_offset_rsp)   \
+M(VF_FLR,              0x006, vf_flr, msg_req, msg_rsp)                \
 /* CGX mbox IDs (range 0x200 - 0x3FF) */                               \
-M(CGX_START_RXTX,      0x200, msg_req, msg_rsp)                        \
-M(CGX_STOP_RXTX,       0x201, msg_req, msg_rsp)                        \
-M(CGX_STATS,           0x202, msg_req, cgx_stats_rsp)                  \
-M(CGX_MAC_ADDR_SET,    0x203, cgx_mac_addr_set_or_get,                 \
+M(CGX_START_RXTX,      0x200, cgx_start_rxtx, msg_req, msg_rsp)        \
+M(CGX_STOP_RXTX,       0x201, cgx_stop_rxtx, msg_req, msg_rsp)         \
+M(CGX_STATS,           0x202, cgx_stats, msg_req, cgx_stats_rsp)       \
+M(CGX_MAC_ADDR_SET,    0x203, cgx_mac_addr_set, cgx_mac_addr_set_or_get,    \
                                cgx_mac_addr_set_or_get)                \
-M(CGX_MAC_ADDR_GET,    0x204, cgx_mac_addr_set_or_get,                 \
+M(CGX_MAC_ADDR_GET,    0x204, cgx_mac_addr_get, cgx_mac_addr_set_or_get,    \
                                cgx_mac_addr_set_or_get)                \
-M(CGX_PROMISC_ENABLE,  0x205, msg_req, msg_rsp)                        \
-M(CGX_PROMISC_DISABLE, 0x206, msg_req, msg_rsp)                        \
-M(CGX_START_LINKEVENTS, 0x207, msg_req, msg_rsp)                       \
-M(CGX_STOP_LINKEVENTS, 0x208, msg_req, msg_rsp)                        \
-M(CGX_GET_LINKINFO,    0x209, msg_req, cgx_link_info_msg)              \
-M(CGX_INTLBK_ENABLE,   0x20A, msg_req, msg_rsp)                        \
-M(CGX_INTLBK_DISABLE,  0x20B, msg_req, msg_rsp)                        \
+M(CGX_PROMISC_ENABLE,  0x205, cgx_promisc_enable, msg_req, msg_rsp)    \
+M(CGX_PROMISC_DISABLE, 0x206, cgx_promisc_disable, msg_req, msg_rsp)   \
+M(CGX_START_LINKEVENTS, 0x207, cgx_start_linkevents, msg_req, msg_rsp) \
+M(CGX_STOP_LINKEVENTS, 0x208, cgx_stop_linkevents, msg_req, msg_rsp)   \
+M(CGX_GET_LINKINFO,    0x209, cgx_get_linkinfo, msg_req, cgx_link_info_msg) \
+M(CGX_INTLBK_ENABLE,   0x20A, cgx_intlbk_enable, msg_req, msg_rsp)     \
+M(CGX_INTLBK_DISABLE,  0x20B, cgx_intlbk_disable, msg_req, msg_rsp)    \
 /* NPA mbox IDs (range 0x400 - 0x5FF) */                               \
-M(NPA_LF_ALLOC,                0x400, npa_lf_alloc_req, npa_lf_alloc_rsp)      \
-M(NPA_LF_FREE,         0x401, msg_req, msg_rsp)                        \
-M(NPA_AQ_ENQ,          0x402, npa_aq_enq_req, npa_aq_enq_rsp)          \
-M(NPA_HWCTX_DISABLE,   0x403, hwctx_disable_req, msg_rsp)              \
+M(NPA_LF_ALLOC,                0x400, npa_lf_alloc,                            \
+                               npa_lf_alloc_req, npa_lf_alloc_rsp)     \
+M(NPA_LF_FREE,         0x401, npa_lf_free, msg_req, msg_rsp)           \
+M(NPA_AQ_ENQ,          0x402, npa_aq_enq, npa_aq_enq_req, npa_aq_enq_rsp)   \
+M(NPA_HWCTX_DISABLE,   0x403, npa_hwctx_disable, hwctx_disable_req, msg_rsp)\
 /* SSO/SSOW mbox IDs (range 0x600 - 0x7FF) */                          \
 /* TIM mbox IDs (range 0x800 - 0x9FF) */                               \
 /* CPT mbox IDs (range 0xA00 - 0xBFF) */                               \
 /* NPC mbox IDs (range 0x6000 - 0x7FFF) */                             \
+M(NPC_MCAM_ALLOC_ENTRY,        0x6000, npc_mcam_alloc_entry, npc_mcam_alloc_entry_req,\
+                               npc_mcam_alloc_entry_rsp)               \
+M(NPC_MCAM_FREE_ENTRY, 0x6001, npc_mcam_free_entry,                    \
+                                npc_mcam_free_entry_req, msg_rsp)      \
+M(NPC_MCAM_WRITE_ENTRY,        0x6002, npc_mcam_write_entry,                   \
+                                npc_mcam_write_entry_req, msg_rsp)     \
+M(NPC_MCAM_ENA_ENTRY,   0x6003, npc_mcam_ena_entry,                    \
+                                npc_mcam_ena_dis_entry_req, msg_rsp)   \
+M(NPC_MCAM_DIS_ENTRY,   0x6004, npc_mcam_dis_entry,                    \
+                                npc_mcam_ena_dis_entry_req, msg_rsp)   \
+M(NPC_MCAM_SHIFT_ENTRY, 0x6005, npc_mcam_shift_entry, npc_mcam_shift_entry_req,\
+                               npc_mcam_shift_entry_rsp)               \
+M(NPC_MCAM_ALLOC_COUNTER, 0x6006, npc_mcam_alloc_counter,              \
+                                       npc_mcam_alloc_counter_req,     \
+                                       npc_mcam_alloc_counter_rsp)     \
+M(NPC_MCAM_FREE_COUNTER,  0x6007, npc_mcam_free_counter,               \
+                                   npc_mcam_oper_counter_req, msg_rsp) \
+M(NPC_MCAM_UNMAP_COUNTER, 0x6008, npc_mcam_unmap_counter,              \
+                                  npc_mcam_unmap_counter_req, msg_rsp) \
+M(NPC_MCAM_CLEAR_COUNTER, 0x6009, npc_mcam_clear_counter,              \
+                                  npc_mcam_oper_counter_req, msg_rsp)  \
+M(NPC_MCAM_COUNTER_STATS, 0x600a, npc_mcam_counter_stats,              \
+                                  npc_mcam_oper_counter_req,           \
+                                  npc_mcam_oper_counter_rsp)           \
+M(NPC_MCAM_ALLOC_AND_WRITE_ENTRY, 0x600b, npc_mcam_alloc_and_write_entry,      \
+                                         npc_mcam_alloc_and_write_entry_req,  \
+                                         npc_mcam_alloc_and_write_entry_rsp)  \
+M(NPC_GET_KEX_CFG,       0x600c, npc_get_kex_cfg,                      \
+                                  msg_req, npc_get_kex_cfg_rsp)        \
 /* NIX mbox IDs (range 0x8000 - 0xFFFF) */                             \
-M(NIX_LF_ALLOC,                0x8000, nix_lf_alloc_req, nix_lf_alloc_rsp)     \
-M(NIX_LF_FREE,         0x8001, msg_req, msg_rsp)                       \
-M(NIX_AQ_ENQ,          0x8002, nix_aq_enq_req, nix_aq_enq_rsp)         \
-M(NIX_HWCTX_DISABLE,   0x8003, hwctx_disable_req, msg_rsp)             \
-M(NIX_TXSCH_ALLOC,     0x8004, nix_txsch_alloc_req, nix_txsch_alloc_rsp) \
-M(NIX_TXSCH_FREE,      0x8005, nix_txsch_free_req, msg_rsp)            \
-M(NIX_TXSCHQ_CFG,      0x8006, nix_txschq_config, msg_rsp)             \
-M(NIX_STATS_RST,       0x8007, msg_req, msg_rsp)                       \
-M(NIX_VTAG_CFG,        0x8008, nix_vtag_config, msg_rsp)               \
-M(NIX_RSS_FLOWKEY_CFG,  0x8009, nix_rss_flowkey_cfg, msg_rsp)          \
-M(NIX_SET_MAC_ADDR,    0x800a, nix_set_mac_addr, msg_rsp)              \
-M(NIX_SET_RX_MODE,     0x800b, nix_rx_mode, msg_rsp)
+M(NIX_LF_ALLOC,                0x8000, nix_lf_alloc,                           \
+                                nix_lf_alloc_req, nix_lf_alloc_rsp)    \
+M(NIX_LF_FREE,         0x8001, nix_lf_free, msg_req, msg_rsp)          \
+M(NIX_AQ_ENQ,          0x8002, nix_aq_enq, nix_aq_enq_req, nix_aq_enq_rsp)  \
+M(NIX_HWCTX_DISABLE,   0x8003, nix_hwctx_disable,                      \
+                                hwctx_disable_req, msg_rsp)            \
+M(NIX_TXSCH_ALLOC,     0x8004, nix_txsch_alloc,                        \
+                                nix_txsch_alloc_req, nix_txsch_alloc_rsp)   \
+M(NIX_TXSCH_FREE,      0x8005, nix_txsch_free, nix_txsch_free_req, msg_rsp) \
+M(NIX_TXSCHQ_CFG,      0x8006, nix_txschq_cfg, nix_txschq_config, msg_rsp)  \
+M(NIX_STATS_RST,       0x8007, nix_stats_rst, msg_req, msg_rsp)        \
+M(NIX_VTAG_CFG,                0x8008, nix_vtag_cfg, nix_vtag_config, msg_rsp) \
+M(NIX_RSS_FLOWKEY_CFG,  0x8009, nix_rss_flowkey_cfg,                   \
+                                nix_rss_flowkey_cfg,                   \
+                                nix_rss_flowkey_cfg_rsp)               \
+M(NIX_SET_MAC_ADDR,    0x800a, nix_set_mac_addr, nix_set_mac_addr, msg_rsp) \
+M(NIX_SET_RX_MODE,     0x800b, nix_set_rx_mode, nix_rx_mode, msg_rsp)  \
+M(NIX_SET_HW_FRS,      0x800c, nix_set_hw_frs, nix_frs_cfg, msg_rsp)   \
+M(NIX_LF_START_RX,     0x800d, nix_lf_start_rx, msg_req, msg_rsp)      \
+M(NIX_LF_STOP_RX,      0x800e, nix_lf_stop_rx, msg_req, msg_rsp)       \
+M(NIX_MARK_FORMAT_CFG, 0x800f, nix_mark_format_cfg,                    \
+                                nix_mark_format_cfg,                   \
+                                nix_mark_format_cfg_rsp)               \
+M(NIX_SET_RX_CFG,      0x8010, nix_set_rx_cfg, nix_rx_cfg, msg_rsp)    \
+M(NIX_LSO_FORMAT_CFG,  0x8011, nix_lso_format_cfg,                     \
+                                nix_lso_format_cfg,                    \
+                                nix_lso_format_cfg_rsp)                \
+M(NIX_RXVLAN_ALLOC,    0x8012, nix_rxvlan_alloc, msg_req, msg_rsp)
 
 /* Messages initiated by AF (range 0xC00 - 0xDFF) */
 #define MBOX_UP_CGX_MESSAGES                                           \
-M(CGX_LINK_EVENT,              0xC00, cgx_link_info_msg, msg_rsp)
+M(CGX_LINK_EVENT,      0xC00, cgx_link_event, cgx_link_info_msg, msg_rsp)
 
 enum {
-#define M(_name, _id, _1, _2) MBOX_MSG_ ## _name = _id,
+#define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,
 MBOX_MESSAGES
 MBOX_UP_CGX_MESSAGES
 #undef M
@@ -191,6 +238,13 @@ struct msg_rsp {
        struct mbox_msghdr hdr;
 };
 
+/* RVU mailbox error codes
+ * Range 256 - 300.
+ */
+enum rvu_af_status {
+       RVU_INVALID_VF_ID           = -256,
+};
+
 struct ready_msg_rsp {
        struct mbox_msghdr hdr;
        u16    sclk_feq;        /* SCLK frequency */
@@ -347,6 +401,8 @@ struct hwctx_disable_req {
        u8 ctype;
 };
 
+/* NIX mbox message formats */
+
 /* NIX mailbox error codes
  * Range 401 - 500.
  */
@@ -365,6 +421,12 @@ enum nix_af_status {
        NIX_AF_INVAL_TXSCHQ_CFG     = -412,
        NIX_AF_SMQ_FLUSH_FAILED     = -413,
        NIX_AF_ERR_LF_RESET         = -414,
+       NIX_AF_ERR_RSS_NOSPC_FIELD  = -415,
+       NIX_AF_ERR_RSS_NOSPC_ALGO   = -416,
+       NIX_AF_ERR_MARK_CFG_FAIL    = -417,
+       NIX_AF_ERR_LSO_CFG_FAIL     = -418,
+       NIX_AF_INVAL_NPA_PF_FUNC    = -419,
+       NIX_AF_INVAL_SSO_PF_FUNC    = -420,
 };
 
 /* For NIX LF context alloc and init */
@@ -392,6 +454,10 @@ struct nix_lf_alloc_rsp {
        u8      lso_tsov4_idx;
        u8      lso_tsov6_idx;
        u8      mac_addr[ETH_ALEN];
+       u8      lf_rx_stats; /* NIX_AF_CONST1::LF_RX_STATS */
+       u8      lf_tx_stats; /* NIX_AF_CONST1::LF_TX_STATS */
+       u16     cints; /* NIX_AF_CONST2::CINTS */
+       u16     qints; /* NIX_AF_CONST2::QINTS */
 };
 
 /* NIX AQ enqueue msg */
@@ -472,6 +538,7 @@ struct nix_txschq_config {
 
 struct nix_vtag_config {
        struct mbox_msghdr hdr;
+       /* '0' for 4 octet VTAG, '1' for 8 octet VTAG */
        u8 vtag_size;
        /* cfg_type is '0' for tx vlan cfg
         * cfg_type is '1' for rx vlan cfg
@@ -492,7 +559,7 @@ struct nix_vtag_config {
 
                /* valid when cfg_type is '1' */
                struct {
-                       /* rx vtag type index */
+                       /* rx vtag type index, valid values are in 0..7 range */
                        u8 vtag_type;
                        /* rx vtag strip */
                        u8 strip_vtag :1;
@@ -505,15 +572,40 @@ struct nix_vtag_config {
 struct nix_rss_flowkey_cfg {
        struct mbox_msghdr hdr;
        int     mcam_index;  /* MCAM entry index to modify */
+#define NIX_FLOW_KEY_TYPE_PORT BIT(0)
+#define NIX_FLOW_KEY_TYPE_IPV4 BIT(1)
+#define NIX_FLOW_KEY_TYPE_IPV6 BIT(2)
+#define NIX_FLOW_KEY_TYPE_TCP  BIT(3)
+#define NIX_FLOW_KEY_TYPE_UDP  BIT(4)
+#define NIX_FLOW_KEY_TYPE_SCTP BIT(5)
        u32     flowkey_cfg; /* Flowkey types selected */
        u8      group;       /* RSS context or group */
 };
 
+struct nix_rss_flowkey_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8      alg_idx; /* Selected algo index */
+};
+
 struct nix_set_mac_addr {
        struct mbox_msghdr hdr;
        u8 mac_addr[ETH_ALEN]; /* MAC address to be set for this pcifunc */
 };
 
+struct nix_mark_format_cfg {
+       struct mbox_msghdr hdr;
+       u8 offset;
+       u8 y_mask;
+       u8 y_val;
+       u8 r_mask;
+       u8 r_val;
+};
+
+struct nix_mark_format_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8 mark_format_idx;
+};
+
 struct nix_rx_mode {
        struct mbox_msghdr hdr;
 #define NIX_RX_MODE_UCAST      BIT(0)
@@ -522,4 +614,182 @@ struct nix_rx_mode {
        u16     mode;
 };
 
+struct nix_rx_cfg {
+       struct mbox_msghdr hdr;
+#define NIX_RX_OL3_VERIFY   BIT(0)
+#define NIX_RX_OL4_VERIFY   BIT(1)
+       u8 len_verify; /* Outer L3/L4 len check */
+#define NIX_RX_CSUM_OL4_VERIFY  BIT(0)
+       u8 csum_verify; /* Outer L4 checksum verification */
+};
+
+struct nix_frs_cfg {
+       struct mbox_msghdr hdr;
+       u8      update_smq;    /* Update SMQ's min/max lens */
+       u8      update_minlen; /* Set minlen also */
+       u8      sdp_link;      /* Set SDP RX link */
+       u16     maxlen;
+       u16     minlen;
+};
+
+struct nix_lso_format_cfg {
+       struct mbox_msghdr hdr;
+       u64 field_mask;
+#define NIX_LSO_FIELD_MAX      8
+       u64 fields[NIX_LSO_FIELD_MAX];
+};
+
+struct nix_lso_format_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u8 lso_format_idx;
+};
+
+/* NPC mbox message structs */
+
+#define NPC_MCAM_ENTRY_INVALID 0xFFFF
+#define NPC_MCAM_INVALID_MAP   0xFFFF
+
+/* NPC mailbox error codes
+ * Range 701 - 800.
+ */
+enum npc_af_status {
+       NPC_MCAM_INVALID_REQ    = -701,
+       NPC_MCAM_ALLOC_DENIED   = -702,
+       NPC_MCAM_ALLOC_FAILED   = -703,
+       NPC_MCAM_PERM_DENIED    = -704,
+};
+
+struct npc_mcam_alloc_entry_req {
+       struct mbox_msghdr hdr;
+#define NPC_MAX_NONCONTIG_ENTRIES      256
+       u8  contig;   /* Contiguous entries ? */
+#define NPC_MCAM_ANY_PRIO              0
+#define NPC_MCAM_LOWER_PRIO            1
+#define NPC_MCAM_HIGHER_PRIO           2
+       u8  priority; /* Lower or higher w.r.t ref_entry */
+       u16 ref_entry;
+       u16 count;    /* Number of entries requested */
+};
+
+struct npc_mcam_alloc_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 entry; /* Entry allocated or start index if contiguous.
+                   * Invalid incase of non-contiguous.
+                   */
+       u16 count; /* Number of entries allocated */
+       u16 free_count; /* Number of entries available */
+       u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
+};
+
+struct npc_mcam_free_entry_req {
+       struct mbox_msghdr hdr;
+       u16 entry; /* Entry index to be freed */
+       u8  all;   /* If all entries allocated to this PFVF to be freed */
+};
+
+struct mcam_entry {
+#define NPC_MAX_KWS_IN_KEY     7 /* Number of keywords in max keywidth */
+       u64     kw[NPC_MAX_KWS_IN_KEY];
+       u64     kw_mask[NPC_MAX_KWS_IN_KEY];
+       u64     action;
+       u64     vtag_action;
+};
+
+struct npc_mcam_write_entry_req {
+       struct mbox_msghdr hdr;
+       struct mcam_entry entry_data;
+       u16 entry;       /* MCAM entry to write this match key */
+       u16 cntr;        /* Counter for this MCAM entry */
+       u8  intf;        /* Rx or Tx interface */
+       u8  enable_entry;/* Enable this MCAM entry ? */
+       u8  set_cntr;    /* Set counter for this entry ? */
+};
+
+/* Enable/Disable a given entry */
+struct npc_mcam_ena_dis_entry_req {
+       struct mbox_msghdr hdr;
+       u16 entry;
+};
+
+struct npc_mcam_shift_entry_req {
+       struct mbox_msghdr hdr;
+#define NPC_MCAM_MAX_SHIFTS    64
+       u16 curr_entry[NPC_MCAM_MAX_SHIFTS];
+       u16 new_entry[NPC_MCAM_MAX_SHIFTS];
+       u16 shift_count; /* Number of entries to shift */
+};
+
+struct npc_mcam_shift_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 failed_entry_idx; /* Index in 'curr_entry', not entry itself */
+};
+
+struct npc_mcam_alloc_counter_req {
+       struct mbox_msghdr hdr;
+       u8  contig;     /* Contiguous counters ? */
+#define NPC_MAX_NONCONTIG_COUNTERS       64
+       u16 count;      /* Number of counters requested */
+};
+
+struct npc_mcam_alloc_counter_rsp {
+       struct mbox_msghdr hdr;
+       u16 cntr;   /* Counter allocated or start index if contiguous.
+                    * Invalid incase of non-contiguous.
+                    */
+       u16 count;  /* Number of counters allocated */
+       u16 cntr_list[NPC_MAX_NONCONTIG_COUNTERS];
+};
+
+struct npc_mcam_oper_counter_req {
+       struct mbox_msghdr hdr;
+       u16 cntr;   /* Free a counter or clear/fetch it's stats */
+};
+
+struct npc_mcam_oper_counter_rsp {
+       struct mbox_msghdr hdr;
+       u64 stat;  /* valid only while fetching counter's stats */
+};
+
+struct npc_mcam_unmap_counter_req {
+       struct mbox_msghdr hdr;
+       u16 cntr;
+       u16 entry; /* Entry and counter to be unmapped */
+       u8  all;   /* Unmap all entries using this counter ? */
+};
+
+struct npc_mcam_alloc_and_write_entry_req {
+       struct mbox_msghdr hdr;
+       struct mcam_entry entry_data;
+       u16 ref_entry;
+       u8  priority;    /* Lower or higher w.r.t ref_entry */
+       u8  intf;        /* Rx or Tx interface */
+       u8  enable_entry;/* Enable this MCAM entry ? */
+       u8  alloc_cntr;  /* Allocate counter and map ? */
+};
+
+struct npc_mcam_alloc_and_write_entry_rsp {
+       struct mbox_msghdr hdr;
+       u16 entry;
+       u16 cntr;
+};
+
+struct npc_get_kex_cfg_rsp {
+       struct mbox_msghdr hdr;
+       u64 rx_keyx_cfg;   /* NPC_AF_INTF(0)_KEX_CFG */
+       u64 tx_keyx_cfg;   /* NPC_AF_INTF(1)_KEX_CFG */
+#define NPC_MAX_INTF   2
+#define NPC_MAX_LID    8
+#define NPC_MAX_LT     16
+#define NPC_MAX_LD     2
+#define NPC_MAX_LFL    16
+       /* NPC_AF_KEX_LDATA(0..1)_FLAGS_CFG */
+       u64 kex_ld_flags[NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LID(0..7)_LT(0..15)_LD(0..1)_CFG */
+       u64 intf_lid_lt_ld[NPC_MAX_INTF][NPC_MAX_LID][NPC_MAX_LT][NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LDATA(0..1)_FLAGS(0..15)_CFG */
+       u64 intf_ld_flags[NPC_MAX_INTF][NPC_MAX_LD][NPC_MAX_LFL];
+#define MKEX_NAME_LEN 128
+       u8 mkex_pfl_name[MKEX_NAME_LEN];
+};
+
 #endif /* MBOX_H */
index f98b011..8d6d90f 100644 (file)
@@ -259,4 +259,28 @@ struct nix_rx_action {
 #endif
 };
 
+/* NIX Receive Vtag Action Structure */
+#define VTAG0_VALID_BIT                BIT_ULL(15)
+#define VTAG0_TYPE_MASK                GENMASK_ULL(14, 12)
+#define VTAG0_LID_MASK         GENMASK_ULL(10, 8)
+#define VTAG0_RELPTR_MASK      GENMASK_ULL(7, 0)
+
+struct npc_mcam_kex {
+       /* MKEX Profle Header */
+       u64 mkex_sign; /* "mcam-kex-profile" (8 bytes/ASCII characters) */
+       u8 name[MKEX_NAME_LEN];   /* MKEX Profile name */
+       u64 cpu_model;   /* Format as profiled by CPU hardware */
+       u64 kpu_version; /* KPU firmware/profile version */
+       u64 reserved; /* Reserved for extension */
+
+       /* MKEX Profle Data */
+       u64 keyx_cfg[NPC_MAX_INTF]; /* NPC_AF_INTF(0..1)_KEX_CFG */
+       /* NPC_AF_KEX_LDATA(0..1)_FLAGS_CFG */
+       u64 kex_ld_flags[NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LID(0..7)_LT(0..15)_LD(0..1)_CFG */
+       u64 intf_lid_lt_ld[NPC_MAX_INTF][NPC_MAX_LID][NPC_MAX_LT][NPC_MAX_LD];
+       /* NPC_AF_INTF(0..1)_LDATA(0..1)_FLAGS(0..15)_CFG */
+       u64 intf_ld_flags[NPC_MAX_INTF][NPC_MAX_LD][NPC_MAX_LFL];
+} __packed;
+
 #endif /* NPC_H */
index dc28fa2..e581091 100644 (file)
@@ -29,6 +29,16 @@ static void rvu_set_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
                                struct rvu_block *block, int lf);
 static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
                                  struct rvu_block *block, int lf);
+static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc);
+
+static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
+                        int type, int num,
+                        void (mbox_handler)(struct work_struct *),
+                        void (mbox_up_handler)(struct work_struct *));
+enum {
+       TYPE_AFVF,
+       TYPE_AFPF,
+};
 
 /* Supported devices */
 static const struct pci_device_id rvu_id_table[] = {
@@ -42,6 +52,10 @@ MODULE_LICENSE("GPL v2");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, rvu_id_table);
 
+static char *mkex_profile; /* MKEX profile name */
+module_param(mkex_profile, charp, 0000);
+MODULE_PARM_DESC(mkex_profile, "MKEX profile name string");
+
 /* Poll a RVU block's register 'offset', for a 'zero'
  * or 'nonzero' at bits specified by 'mask'
  */
@@ -153,17 +167,17 @@ int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot)
        u16 match = 0;
        int lf;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lf = 0; lf < block->lf.max; lf++) {
                if (block->fn_map[lf] == pcifunc) {
                        if (slot == match) {
-                               spin_unlock(&rvu->rsrc_lock);
+                               mutex_unlock(&rvu->rsrc_lock);
                                return lf;
                        }
                        match++;
                }
        }
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return -ENODEV;
 }
 
@@ -337,6 +351,28 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc)
                return &rvu->pf[rvu_get_pf(pcifunc)];
 }
 
+static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
+{
+       int pf, vf, nvfs;
+       u64 cfg;
+
+       pf = rvu_get_pf(pcifunc);
+       if (pf >= rvu->hw->total_pfs)
+               return false;
+
+       if (!(pcifunc & RVU_PFVF_FUNC_MASK))
+               return true;
+
+       /* Check if VF is within number of VFs attached to this PF */
+       vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
+       cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+       nvfs = (cfg >> 12) & 0xFF;
+       if (vf >= nvfs)
+               return false;
+
+       return true;
+}
+
 bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr)
 {
        struct rvu_block *block;
@@ -597,6 +633,8 @@ static void rvu_free_hw_resources(struct rvu *rvu)
        dma_unmap_resource(rvu->dev, rvu->msix_base_iova,
                           max_msix * PCI_MSIX_ENTRY_SIZE,
                           DMA_BIDIRECTIONAL, 0);
+
+       mutex_destroy(&rvu->rsrc_lock);
 }
 
 static int rvu_setup_hw_resources(struct rvu *rvu)
@@ -752,7 +790,7 @@ init:
        if (!rvu->hwvf)
                return -ENOMEM;
 
-       spin_lock_init(&rvu->rsrc_lock);
+       mutex_init(&rvu->rsrc_lock);
 
        err = rvu_setup_msix_resources(rvu);
        if (err)
@@ -777,17 +815,26 @@ init:
 
        err = rvu_npc_init(rvu);
        if (err)
-               return err;
+               goto exit;
+
+       err = rvu_cgx_init(rvu);
+       if (err)
+               goto exit;
 
        err = rvu_npa_init(rvu);
        if (err)
-               return err;
+               goto cgx_err;
 
        err = rvu_nix_init(rvu);
        if (err)
-               return err;
+               goto cgx_err;
 
        return 0;
+
+cgx_err:
+       rvu_cgx_exit(rvu);
+exit:
+       return err;
 }
 
 /* NPA and NIX admin queue APIs */
@@ -830,7 +877,7 @@ int rvu_aq_alloc(struct rvu *rvu, struct admin_queue **ad_queue,
        return 0;
 }
 
-static int rvu_mbox_handler_READY(struct rvu *rvu, struct msg_req *req,
+static int rvu_mbox_handler_ready(struct rvu *rvu, struct msg_req *req,
                                  struct ready_msg_rsp *rsp)
 {
        return 0;
@@ -858,6 +905,22 @@ static u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blktype)
        return 0;
 }
 
+bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype)
+{
+       struct rvu_pfvf *pfvf;
+
+       if (!is_pf_func_valid(rvu, pcifunc))
+               return false;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+       /* Check if this PFFUNC has a LF of type blktype attached */
+       if (!rvu_get_rsrc_mapcount(pfvf, blktype))
+               return false;
+
+       return true;
+}
+
 static int rvu_lookup_rsrc(struct rvu *rvu, struct rvu_block *block,
                           int pcifunc, int slot)
 {
@@ -926,7 +989,7 @@ static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach,
        struct rvu_block *block;
        int blkid;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
 
        /* Check for partial resource detach */
        if (detach && detach->partial)
@@ -956,11 +1019,11 @@ static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach,
                rvu_detach_block(rvu, pcifunc, block->type);
        }
 
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return 0;
 }
 
-static int rvu_mbox_handler_DETACH_RESOURCES(struct rvu *rvu,
+static int rvu_mbox_handler_detach_resources(struct rvu *rvu,
                                             struct rsrc_detach *detach,
                                             struct msg_rsp *rsp)
 {
@@ -1108,7 +1171,7 @@ fail:
        return -ENOSPC;
 }
 
-static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
+static int rvu_mbox_handler_attach_resources(struct rvu *rvu,
                                             struct rsrc_attach *attach,
                                             struct msg_rsp *rsp)
 {
@@ -1119,7 +1182,7 @@ static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
        if (!attach->modify)
                rvu_detach_rsrcs(rvu, NULL, pcifunc);
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
 
        /* Check if the request can be accommodated */
        err = rvu_check_rsrc_availability(rvu, attach, pcifunc);
@@ -1163,7 +1226,7 @@ static int rvu_mbox_handler_ATTACH_RESOURCES(struct rvu *rvu,
        }
 
 exit:
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return err;
 }
 
@@ -1231,7 +1294,7 @@ static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
        rvu_free_rsrc_contig(&pfvf->msix, nvecs, offset);
 }
 
-static int rvu_mbox_handler_MSIX_OFFSET(struct rvu *rvu, struct msg_req *req,
+static int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
                                        struct msix_offset_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -1280,22 +1343,51 @@ static int rvu_mbox_handler_MSIX_OFFSET(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
+static int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req,
+                                  struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 vf, numvfs;
+       u64 cfg;
+
+       vf = pcifunc & RVU_PFVF_FUNC_MASK;
+       cfg = rvu_read64(rvu, BLKADDR_RVUM,
+                        RVU_PRIV_PFX_CFG(rvu_get_pf(pcifunc)));
+       numvfs = (cfg >> 12) & 0xFF;
+
+       if (vf && vf <= numvfs)
+               __rvu_flr_handler(rvu, pcifunc);
+       else
+               return RVU_INVALID_VF_ID;
+
+       return 0;
+}
+
+static int rvu_process_mbox_msg(struct otx2_mbox *mbox, int devid,
                                struct mbox_msghdr *req)
 {
+       struct rvu *rvu = pci_get_drvdata(mbox->pdev);
+
        /* Check if valid, if not reply with a invalid msg */
        if (req->sig != OTX2_MBOX_REQ_SIG)
                goto bad_message;
 
        switch (req->id) {
-#define M(_name, _id, _req_type, _rsp_type)                            \
+#define M(_name, _id, _fn_name, _req_type, _rsp_type)                  \
        case _id: {                                                     \
                struct _rsp_type *rsp;                                  \
                int err;                                                \
                                                                        \
                rsp = (struct _rsp_type *)otx2_mbox_alloc_msg(          \
-                       &rvu->mbox, devid,                              \
+                       mbox, devid,                                    \
                        sizeof(struct _rsp_type));                      \
+               /* some handlers should complete even if reply */       \
+               /* could not be allocated */                            \
+               if (!rsp &&                                             \
+                   _id != MBOX_MSG_DETACH_RESOURCES &&                 \
+                   _id != MBOX_MSG_NIX_TXSCH_FREE &&                   \
+                   _id != MBOX_MSG_VF_FLR)                             \
+                       return -ENOMEM;                                 \
                if (rsp) {                                              \
                        rsp->hdr.id = _id;                              \
                        rsp->hdr.sig = OTX2_MBOX_RSP_SIG;               \
@@ -1303,9 +1395,9 @@ static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
                        rsp->hdr.rc = 0;                                \
                }                                                       \
                                                                        \
-               err = rvu_mbox_handler_ ## _name(rvu,                   \
-                                                (struct _req_type *)req, \
-                                                rsp);                  \
+               err = rvu_mbox_handler_ ## _fn_name(rvu,                \
+                                                   (struct _req_type *)req, \
+                                                   rsp);               \
                if (rsp && err)                                         \
                        rsp->hdr.rc = err;                              \
                                                                        \
@@ -1313,29 +1405,38 @@ static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
        }
 MBOX_MESSAGES
 #undef M
-               break;
+
 bad_message:
        default:
-               otx2_reply_invalid_msg(&rvu->mbox, devid, req->pcifunc,
-                                      req->id);
+               otx2_reply_invalid_msg(mbox, devid, req->pcifunc, req->id);
                return -ENODEV;
        }
 }
 
-static void rvu_mbox_handler(struct work_struct *work)
+static void __rvu_mbox_handler(struct rvu_work *mwork, int type)
 {
-       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
        struct rvu *rvu = mwork->rvu;
+       int offset, err, id, devid;
        struct otx2_mbox_dev *mdev;
        struct mbox_hdr *req_hdr;
        struct mbox_msghdr *msg;
+       struct mbox_wq_info *mw;
        struct otx2_mbox *mbox;
-       int offset, id, err;
-       u16 pf;
 
-       mbox = &rvu->mbox;
-       pf = mwork - rvu->mbox_wrk;
-       mdev = &mbox->dev[pf];
+       switch (type) {
+       case TYPE_AFPF:
+               mw = &rvu->afpf_wq_info;
+               break;
+       case TYPE_AFVF:
+               mw = &rvu->afvf_wq_info;
+               break;
+       default:
+               return;
+       }
+
+       devid = mwork - mw->mbox_wrk;
+       mbox = &mw->mbox;
+       mdev = &mbox->dev[devid];
 
        /* Process received mbox messages */
        req_hdr = mdev->mbase + mbox->rx_start;
@@ -1347,10 +1448,21 @@ static void rvu_mbox_handler(struct work_struct *work)
        for (id = 0; id < req_hdr->num_msgs; id++) {
                msg = mdev->mbase + offset;
 
-               /* Set which PF sent this message based on mbox IRQ */
-               msg->pcifunc &= ~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
-               msg->pcifunc |= (pf << RVU_PFVF_PF_SHIFT);
-               err = rvu_process_mbox_msg(rvu, pf, msg);
+               /* Set which PF/VF sent this message based on mbox IRQ */
+               switch (type) {
+               case TYPE_AFPF:
+                       msg->pcifunc &=
+                               ~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
+                       msg->pcifunc |= (devid << RVU_PFVF_PF_SHIFT);
+                       break;
+               case TYPE_AFVF:
+                       msg->pcifunc &=
+                               ~(RVU_PFVF_FUNC_MASK << RVU_PFVF_FUNC_SHIFT);
+                       msg->pcifunc |= (devid << RVU_PFVF_FUNC_SHIFT) + 1;
+                       break;
+               }
+
+               err = rvu_process_mbox_msg(mbox, devid, msg);
                if (!err) {
                        offset = mbox->rx_start + msg->next_msgoff;
                        continue;
@@ -1358,31 +1470,57 @@ static void rvu_mbox_handler(struct work_struct *work)
 
                if (msg->pcifunc & RVU_PFVF_FUNC_MASK)
                        dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d:VF%d\n",
-                                err, otx2_mbox_id2name(msg->id), msg->id, pf,
+                                err, otx2_mbox_id2name(msg->id),
+                                msg->id, devid,
                                 (msg->pcifunc & RVU_PFVF_FUNC_MASK) - 1);
                else
                        dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d\n",
-                                err, otx2_mbox_id2name(msg->id), msg->id, pf);
+                                err, otx2_mbox_id2name(msg->id),
+                                msg->id, devid);
        }
 
-       /* Send mbox responses to PF */
-       otx2_mbox_msg_send(mbox, pf);
+       /* Send mbox responses to VF/PF */
+       otx2_mbox_msg_send(mbox, devid);
+}
+
+static inline void rvu_afpf_mbox_handler(struct work_struct *work)
+{
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_handler(mwork, TYPE_AFPF);
 }
 
-static void rvu_mbox_up_handler(struct work_struct *work)
+static inline void rvu_afvf_mbox_handler(struct work_struct *work)
 {
        struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_handler(mwork, TYPE_AFVF);
+}
+
+static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)
+{
        struct rvu *rvu = mwork->rvu;
        struct otx2_mbox_dev *mdev;
        struct mbox_hdr *rsp_hdr;
        struct mbox_msghdr *msg;
+       struct mbox_wq_info *mw;
        struct otx2_mbox *mbox;
-       int offset, id;
-       u16 pf;
+       int offset, id, devid;
+
+       switch (type) {
+       case TYPE_AFPF:
+               mw = &rvu->afpf_wq_info;
+               break;
+       case TYPE_AFVF:
+               mw = &rvu->afvf_wq_info;
+               break;
+       default:
+               return;
+       }
 
-       mbox = &rvu->mbox_up;
-       pf = mwork - rvu->mbox_wrk_up;
-       mdev = &mbox->dev[pf];
+       devid = mwork - mw->mbox_wrk_up;
+       mbox = &mw->mbox_up;
+       mdev = &mbox->dev[devid];
 
        rsp_hdr = mdev->mbase + mbox->rx_start;
        if (rsp_hdr->num_msgs == 0) {
@@ -1423,128 +1561,182 @@ end:
                mdev->msgs_acked++;
        }
 
-       otx2_mbox_reset(mbox, 0);
+       otx2_mbox_reset(mbox, devid);
 }
 
-static int rvu_mbox_init(struct rvu *rvu)
+static inline void rvu_afpf_mbox_up_handler(struct work_struct *work)
 {
-       struct rvu_hwinfo *hw = rvu->hw;
-       void __iomem *hwbase = NULL;
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_up_handler(mwork, TYPE_AFPF);
+}
+
+static inline void rvu_afvf_mbox_up_handler(struct work_struct *work)
+{
+       struct rvu_work *mwork = container_of(work, struct rvu_work, work);
+
+       __rvu_mbox_up_handler(mwork, TYPE_AFVF);
+}
+
+static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
+                        int type, int num,
+                        void (mbox_handler)(struct work_struct *),
+                        void (mbox_up_handler)(struct work_struct *))
+{
+       void __iomem *hwbase = NULL, *reg_base;
+       int err, i, dir, dir_up;
        struct rvu_work *mwork;
+       const char *name;
        u64 bar4_addr;
-       int err, pf;
 
-       rvu->mbox_wq = alloc_workqueue("rvu_afpf_mailbox",
-                                      WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
-                                      hw->total_pfs);
-       if (!rvu->mbox_wq)
+       switch (type) {
+       case TYPE_AFPF:
+               name = "rvu_afpf_mailbox";
+               bar4_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PF_BAR4_ADDR);
+               dir = MBOX_DIR_AFPF;
+               dir_up = MBOX_DIR_AFPF_UP;
+               reg_base = rvu->afreg_base;
+               break;
+       case TYPE_AFVF:
+               name = "rvu_afvf_mailbox";
+               bar4_addr = rvupf_read64(rvu, RVU_PF_VF_BAR4_ADDR);
+               dir = MBOX_DIR_PFVF;
+               dir_up = MBOX_DIR_PFVF_UP;
+               reg_base = rvu->pfreg_base;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mw->mbox_wq = alloc_workqueue(name,
+                                     WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                                     num);
+       if (!mw->mbox_wq)
                return -ENOMEM;
 
-       rvu->mbox_wrk = devm_kcalloc(rvu->dev, hw->total_pfs,
-                                    sizeof(struct rvu_work), GFP_KERNEL);
-       if (!rvu->mbox_wrk) {
+       mw->mbox_wrk = devm_kcalloc(rvu->dev, num,
+                                   sizeof(struct rvu_work), GFP_KERNEL);
+       if (!mw->mbox_wrk) {
                err = -ENOMEM;
                goto exit;
        }
 
-       rvu->mbox_wrk_up = devm_kcalloc(rvu->dev, hw->total_pfs,
-                                       sizeof(struct rvu_work), GFP_KERNEL);
-       if (!rvu->mbox_wrk_up) {
+       mw->mbox_wrk_up = devm_kcalloc(rvu->dev, num,
+                                      sizeof(struct rvu_work), GFP_KERNEL);
+       if (!mw->mbox_wrk_up) {
                err = -ENOMEM;
                goto exit;
        }
 
-       /* Map mbox region shared with PFs */
-       bar4_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PF_BAR4_ADDR);
        /* Mailbox is a reserved memory (in RAM) region shared between
         * RVU devices, shouldn't be mapped as device memory to allow
         * unaligned accesses.
         */
-       hwbase = ioremap_wc(bar4_addr, MBOX_SIZE * hw->total_pfs);
+       hwbase = ioremap_wc(bar4_addr, MBOX_SIZE * num);
        if (!hwbase) {
                dev_err(rvu->dev, "Unable to map mailbox region\n");
                err = -ENOMEM;
                goto exit;
        }
 
-       err = otx2_mbox_init(&rvu->mbox, hwbase, rvu->pdev, rvu->afreg_base,
-                            MBOX_DIR_AFPF, hw->total_pfs);
+       err = otx2_mbox_init(&mw->mbox, hwbase, rvu->pdev, reg_base, dir, num);
        if (err)
                goto exit;
 
-       err = otx2_mbox_init(&rvu->mbox_up, hwbase, rvu->pdev, rvu->afreg_base,
-                            MBOX_DIR_AFPF_UP, hw->total_pfs);
+       err = otx2_mbox_init(&mw->mbox_up, hwbase, rvu->pdev,
+                            reg_base, dir_up, num);
        if (err)
                goto exit;
 
-       for (pf = 0; pf < hw->total_pfs; pf++) {
-               mwork = &rvu->mbox_wrk[pf];
+       for (i = 0; i < num; i++) {
+               mwork = &mw->mbox_wrk[i];
                mwork->rvu = rvu;
-               INIT_WORK(&mwork->work, rvu_mbox_handler);
-       }
+               INIT_WORK(&mwork->work, mbox_handler);
 
-       for (pf = 0; pf < hw->total_pfs; pf++) {
-               mwork = &rvu->mbox_wrk_up[pf];
+               mwork = &mw->mbox_wrk_up[i];
                mwork->rvu = rvu;
-               INIT_WORK(&mwork->work, rvu_mbox_up_handler);
+               INIT_WORK(&mwork->work, mbox_up_handler);
        }
 
        return 0;
 exit:
        if (hwbase)
                iounmap((void __iomem *)hwbase);
-       destroy_workqueue(rvu->mbox_wq);
+       destroy_workqueue(mw->mbox_wq);
        return err;
 }
 
-static void rvu_mbox_destroy(struct rvu *rvu)
+static void rvu_mbox_destroy(struct mbox_wq_info *mw)
 {
-       if (rvu->mbox_wq) {
-               flush_workqueue(rvu->mbox_wq);
-               destroy_workqueue(rvu->mbox_wq);
-               rvu->mbox_wq = NULL;
+       if (mw->mbox_wq) {
+               flush_workqueue(mw->mbox_wq);
+               destroy_workqueue(mw->mbox_wq);
+               mw->mbox_wq = NULL;
        }
 
-       if (rvu->mbox.hwbase)
-               iounmap((void __iomem *)rvu->mbox.hwbase);
+       if (mw->mbox.hwbase)
+               iounmap((void __iomem *)mw->mbox.hwbase);
 
-       otx2_mbox_destroy(&rvu->mbox);
-       otx2_mbox_destroy(&rvu->mbox_up);
+       otx2_mbox_destroy(&mw->mbox);
+       otx2_mbox_destroy(&mw->mbox_up);
 }
 
-static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
+static void rvu_queue_work(struct mbox_wq_info *mw, int first,
+                          int mdevs, u64 intr)
 {
-       struct rvu *rvu = (struct rvu *)rvu_irq;
        struct otx2_mbox_dev *mdev;
        struct otx2_mbox *mbox;
        struct mbox_hdr *hdr;
+       int i;
+
+       for (i = first; i < mdevs; i++) {
+               /* start from 0 */
+               if (!(intr & BIT_ULL(i - first)))
+                       continue;
+
+               mbox = &mw->mbox;
+               mdev = &mbox->dev[i];
+               hdr = mdev->mbase + mbox->rx_start;
+               if (hdr->num_msgs)
+                       queue_work(mw->mbox_wq, &mw->mbox_wrk[i].work);
+
+               mbox = &mw->mbox_up;
+               mdev = &mbox->dev[i];
+               hdr = mdev->mbase + mbox->rx_start;
+               if (hdr->num_msgs)
+                       queue_work(mw->mbox_wq, &mw->mbox_wrk_up[i].work);
+       }
+}
+
+static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       int vfs = rvu->vfs;
        u64 intr;
-       u8  pf;
 
        intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT);
        /* Clear interrupts */
        rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT, intr);
 
        /* Sync with mbox memory region */
-       smp_wmb();
+       rmb();
 
-       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
-               if (intr & (1ULL << pf)) {
-                       mbox = &rvu->mbox;
-                       mdev = &mbox->dev[pf];
-                       hdr = mdev->mbase + mbox->rx_start;
-                       if (hdr->num_msgs)
-                               queue_work(rvu->mbox_wq,
-                                          &rvu->mbox_wrk[pf].work);
-                       mbox = &rvu->mbox_up;
-                       mdev = &mbox->dev[pf];
-                       hdr = mdev->mbase + mbox->rx_start;
-                       if (hdr->num_msgs)
-                               queue_work(rvu->mbox_wq,
-                                          &rvu->mbox_wrk_up[pf].work);
-               }
+       rvu_queue_work(&rvu->afpf_wq_info, 0, rvu->hw->total_pfs, intr);
+
+       /* Handle VF interrupts */
+       if (vfs > 64) {
+               intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(1));
+               rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), intr);
+
+               rvu_queue_work(&rvu->afvf_wq_info, 64, vfs, intr);
+               vfs -= 64;
        }
 
+       intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(0));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), intr);
+
+       rvu_queue_work(&rvu->afvf_wq_info, 0, vfs, intr);
+
        return IRQ_HANDLED;
 }
 
@@ -1561,6 +1753,216 @@ static void rvu_enable_mbox_intr(struct rvu *rvu)
                    INTR_MASK(hw->total_pfs) & ~1ULL);
 }
 
+static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr)
+{
+       struct rvu_block *block;
+       int slot, lf, num_lfs;
+       int err;
+
+       block = &rvu->hw->block[blkaddr];
+       num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc),
+                                       block->type);
+       if (!num_lfs)
+               return;
+       for (slot = 0; slot < num_lfs; slot++) {
+               lf = rvu_get_lf(rvu, block, pcifunc, slot);
+               if (lf < 0)
+                       continue;
+
+               /* Cleanup LF and reset it */
+               if (block->addr == BLKADDR_NIX0)
+                       rvu_nix_lf_teardown(rvu, pcifunc, block->addr, lf);
+               else if (block->addr == BLKADDR_NPA)
+                       rvu_npa_lf_teardown(rvu, pcifunc, lf);
+
+               err = rvu_lf_reset(rvu, block, lf);
+               if (err) {
+                       dev_err(rvu->dev, "Failed to reset blkaddr %d LF%d\n",
+                               block->addr, lf);
+               }
+       }
+}
+
+static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc)
+{
+       mutex_lock(&rvu->flr_lock);
+       /* Reset order should reflect inter-block dependencies:
+        * 1. Reset any packet/work sources (NIX, CPT, TIM)
+        * 2. Flush and reset SSO/SSOW
+        * 3. Cleanup pools (NPA)
+        */
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NIX0);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_CPT0);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_TIM);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSOW);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSO);
+       rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA);
+       rvu_detach_rsrcs(rvu, NULL, pcifunc);
+       mutex_unlock(&rvu->flr_lock);
+}
+
+static void rvu_afvf_flr_handler(struct rvu *rvu, int vf)
+{
+       int reg = 0;
+
+       /* pcifunc = 0(PF0) | (vf + 1) */
+       __rvu_flr_handler(rvu, vf + 1);
+
+       if (vf >= 64) {
+               reg = 1;
+               vf = vf - 64;
+       }
+
+       /* Signal FLR finish and enable IRQ */
+       rvupf_write64(rvu, RVU_PF_VFTRPENDX(reg), BIT_ULL(vf));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf));
+}
+
+static void rvu_flr_handler(struct work_struct *work)
+{
+       struct rvu_work *flrwork = container_of(work, struct rvu_work, work);
+       struct rvu *rvu = flrwork->rvu;
+       u16 pcifunc, numvfs, vf;
+       u64 cfg;
+       int pf;
+
+       pf = flrwork - rvu->flr_wrk;
+       if (pf >= rvu->hw->total_pfs) {
+               rvu_afvf_flr_handler(rvu, pf - rvu->hw->total_pfs);
+               return;
+       }
+
+       cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+       numvfs = (cfg >> 12) & 0xFF;
+       pcifunc  = pf << RVU_PFVF_PF_SHIFT;
+
+       for (vf = 0; vf < numvfs; vf++)
+               __rvu_flr_handler(rvu, (pcifunc | (vf + 1)));
+
+       __rvu_flr_handler(rvu, pcifunc);
+
+       /* Signal FLR finish */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND, BIT_ULL(pf));
+
+       /* Enable interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S,  BIT_ULL(pf));
+}
+
+static void rvu_afvf_queue_flr_work(struct rvu *rvu, int start_vf, int numvfs)
+{
+       int dev, vf, reg = 0;
+       u64 intr;
+
+       if (start_vf >= 64)
+               reg = 1;
+
+       intr = rvupf_read64(rvu, RVU_PF_VFFLR_INTX(reg));
+       if (!intr)
+               return;
+
+       for (vf = 0; vf < numvfs; vf++) {
+               if (!(intr & BIT_ULL(vf)))
+                       continue;
+               dev = vf + start_vf + rvu->hw->total_pfs;
+               queue_work(rvu->flr_wq, &rvu->flr_wrk[dev].work);
+               /* Clear and disable the interrupt */
+               rvupf_write64(rvu, RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf));
+               rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(reg), BIT_ULL(vf));
+       }
+}
+
+static irqreturn_t rvu_flr_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       u64 intr;
+       u8  pf;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT);
+       if (!intr)
+               goto afvf_flr;
+
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               if (intr & (1ULL << pf)) {
+                       /* PF is already dead do only AF related operations */
+                       queue_work(rvu->flr_wq, &rvu->flr_wrk[pf].work);
+                       /* clear interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT,
+                                   BIT_ULL(pf));
+                       /* Disable the interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
+                                   BIT_ULL(pf));
+               }
+       }
+
+afvf_flr:
+       rvu_afvf_queue_flr_work(rvu, 0, 64);
+       if (rvu->vfs > 64)
+               rvu_afvf_queue_flr_work(rvu, 64, rvu->vfs - 64);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_me_handle_vfset(struct rvu *rvu, int idx, u64 intr)
+{
+       int vf;
+
+       /* Nothing to be done here other than clearing the
+        * TRPEND bit.
+        */
+       for (vf = 0; vf < 64; vf++) {
+               if (intr & (1ULL << vf)) {
+                       /* clear the trpend due to ME(master enable) */
+                       rvupf_write64(rvu, RVU_PF_VFTRPENDX(idx), BIT_ULL(vf));
+                       /* clear interrupt */
+                       rvupf_write64(rvu, RVU_PF_VFME_INTX(idx), BIT_ULL(vf));
+               }
+       }
+}
+
+/* Handles ME interrupts from VFs of AF */
+static irqreturn_t rvu_me_vf_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       int vfset;
+       u64 intr;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
+
+       for (vfset = 0; vfset <= 1; vfset++) {
+               intr = rvupf_read64(rvu, RVU_PF_VFME_INTX(vfset));
+               if (intr)
+                       rvu_me_handle_vfset(rvu, vfset, intr);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/* Handles ME interrupts from PFs */
+static irqreturn_t rvu_me_pf_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu *rvu = (struct rvu *)rvu_irq;
+       u64 intr;
+       u8  pf;
+
+       intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
+
+       /* Nothing to be done here other than clearing the
+        * TRPEND bit.
+        */
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               if (intr & (1ULL << pf)) {
+                       /* clear the trpend due to ME(master enable) */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND,
+                                   BIT_ULL(pf));
+                       /* clear interrupt */
+                       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT,
+                                   BIT_ULL(pf));
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
 static void rvu_unregister_interrupts(struct rvu *rvu)
 {
        int irq;
@@ -1569,6 +1971,14 @@ static void rvu_unregister_interrupts(struct rvu *rvu)
        rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1C,
                    INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
 
+       /* Disable the PF FLR interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       /* Disable the PF ME interrupt */
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1C,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
        for (irq = 0; irq < rvu->num_vec; irq++) {
                if (rvu->irq_allocated[irq])
                        free_irq(pci_irq_vector(rvu->pdev, irq), rvu);
@@ -1578,9 +1988,25 @@ static void rvu_unregister_interrupts(struct rvu *rvu)
        rvu->num_vec = 0;
 }
 
+static int rvu_afvf_msix_vectors_num_ok(struct rvu *rvu)
+{
+       struct rvu_pfvf *pfvf = &rvu->pf[0];
+       int offset;
+
+       pfvf = &rvu->pf[0];
+       offset = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
+
+       /* Make sure there are enough MSIX vectors configured so that
+        * VF interrupts can be handled. Offset equal to zero means
+        * that PF vectors are not configured and overlapping AF vectors.
+        */
+       return (pfvf->msix.max >= RVU_AF_INT_VEC_CNT + RVU_PF_INT_VEC_CNT) &&
+              offset;
+}
+
 static int rvu_register_interrupts(struct rvu *rvu)
 {
-       int ret;
+       int ret, offset, pf_vec_start;
 
        rvu->num_vec = pci_msix_vec_count(rvu->pdev);
 
@@ -1620,13 +2046,331 @@ static int rvu_register_interrupts(struct rvu *rvu)
        /* Enable mailbox interrupts from all PFs */
        rvu_enable_mbox_intr(rvu);
 
+       /* Register FLR interrupt handler */
+       sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
+               "RVUAF FLR");
+       ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFFLR),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
+                         rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for FLR\n");
+               goto fail;
+       }
+       rvu->irq_allocated[RVU_AF_INT_VEC_PFFLR] = true;
+
+       /* Enable FLR interrupt for all PFs*/
+       rvu_write64(rvu, BLKADDR_RVUM,
+                   RVU_AF_PFFLR_INT, INTR_MASK(rvu->hw->total_pfs));
+
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       /* Register ME interrupt handler */
+       sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
+               "RVUAF ME");
+       ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFME),
+                         rvu_me_pf_intr_handler, 0,
+                         &rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
+                         rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for ME\n");
+       }
+       rvu->irq_allocated[RVU_AF_INT_VEC_PFME] = true;
+
+       /* Enable ME interrupt for all PFs*/
+       rvu_write64(rvu, BLKADDR_RVUM,
+                   RVU_AF_PFME_INT, INTR_MASK(rvu->hw->total_pfs));
+
+       rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1S,
+                   INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
+
+       if (!rvu_afvf_msix_vectors_num_ok(rvu))
+               return 0;
+
+       /* Get PF MSIX vectors offset. */
+       pf_vec_start = rvu_read64(rvu, BLKADDR_RVUM,
+                                 RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
+
+       /* Register MBOX0 interrupt. */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_mbox_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE],
+                         rvu);
+       if (ret)
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for Mbox0\n");
+
+       rvu->irq_allocated[offset] = true;
+
+       /* Register MBOX1 interrupt. MBOX1 IRQ number follows MBOX0 so
+        * simply increment current offset by 1.
+        */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_mbox_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE],
+                         rvu);
+       if (ret)
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for Mbox1\n");
+
+       rvu->irq_allocated[offset] = true;
+
+       /* Register FLR interrupt handler for AF's VFs */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF FLR0\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_flr_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF FLR1\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       /* Register ME interrupt handler for AF's VFs */
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFME0;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME0");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_me_vf_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF ME0\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
+
+       offset = pf_vec_start + RVU_PF_INT_VEC_VFME1;
+       sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME1");
+       ret = request_irq(pci_irq_vector(rvu->pdev, offset),
+                         rvu_me_vf_intr_handler, 0,
+                         &rvu->irq_name[offset * NAME_SIZE], rvu);
+       if (ret) {
+               dev_err(rvu->dev,
+                       "RVUAF: IRQ registration failed for RVUAFVF ME1\n");
+               goto fail;
+       }
+       rvu->irq_allocated[offset] = true;
        return 0;
 
 fail:
-       pci_free_irq_vectors(rvu->pdev);
+       rvu_unregister_interrupts(rvu);
+       return ret;
+}
+
+static void rvu_flr_wq_destroy(struct rvu *rvu)
+{
+       if (rvu->flr_wq) {
+               flush_workqueue(rvu->flr_wq);
+               destroy_workqueue(rvu->flr_wq);
+               rvu->flr_wq = NULL;
+       }
+}
+
+static int rvu_flr_init(struct rvu *rvu)
+{
+       int dev, num_devs;
+       u64 cfg;
+       int pf;
+
+       /* Enable FLR for all PFs*/
+       for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+               cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+               rvu_write64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf),
+                           cfg | BIT_ULL(22));
+       }
+
+       rvu->flr_wq = alloc_workqueue("rvu_afpf_flr",
+                                     WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                                      1);
+       if (!rvu->flr_wq)
+               return -ENOMEM;
+
+       num_devs = rvu->hw->total_pfs + pci_sriov_get_totalvfs(rvu->pdev);
+       rvu->flr_wrk = devm_kcalloc(rvu->dev, num_devs,
+                                   sizeof(struct rvu_work), GFP_KERNEL);
+       if (!rvu->flr_wrk) {
+               destroy_workqueue(rvu->flr_wq);
+               return -ENOMEM;
+       }
+
+       for (dev = 0; dev < num_devs; dev++) {
+               rvu->flr_wrk[dev].rvu = rvu;
+               INIT_WORK(&rvu->flr_wrk[dev].work, rvu_flr_handler);
+       }
+
+       mutex_init(&rvu->flr_lock);
+
+       return 0;
+}
+
+static void rvu_disable_afvf_intr(struct rvu *rvu)
+{
+       int vfs = rvu->vfs;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(vfs));
+       if (vfs <= 64)
+               return;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1),
+                     INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
+}
+
+static void rvu_enable_afvf_intr(struct rvu *rvu)
+{
+       int vfs = rvu->vfs;
+
+       /* Clear any pending interrupts and enable AF VF interrupts for
+        * the first 64 VFs.
+        */
+       /* Mbox */
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), INTR_MASK(vfs));
+
+       /* FLR */
+       rvupf_write64(rvu, RVU_PF_VFFLR_INTX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(vfs));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(vfs));
+
+       /* Same for remaining VFs, if any. */
+       if (vfs <= 64)
+               return;
+
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1),
+                     INTR_MASK(vfs - 64));
+
+       rvupf_write64(rvu, RVU_PF_VFFLR_INTX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
+       rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
+}
+
+#define PCI_DEVID_OCTEONTX2_LBK 0xA061
+
+static int lbk_get_num_chans(void)
+{
+       struct pci_dev *pdev;
+       void __iomem *base;
+       int ret = -EIO;
+
+       pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_LBK,
+                             NULL);
+       if (!pdev)
+               goto err;
+
+       base = pci_ioremap_bar(pdev, 0);
+       if (!base)
+               goto err_put;
+
+       /* Read number of available LBK channels from LBK(0)_CONST register. */
+       ret = (readq(base + 0x10) >> 32) & 0xffff;
+       iounmap(base);
+err_put:
+       pci_dev_put(pdev);
+err:
        return ret;
 }
 
+static int rvu_enable_sriov(struct rvu *rvu)
+{
+       struct pci_dev *pdev = rvu->pdev;
+       int err, chans, vfs;
+
+       if (!rvu_afvf_msix_vectors_num_ok(rvu)) {
+               dev_warn(&pdev->dev,
+                        "Skipping SRIOV enablement since not enough IRQs are available\n");
+               return 0;
+       }
+
+       chans = lbk_get_num_chans();
+       if (chans < 0)
+               return chans;
+
+       vfs = pci_sriov_get_totalvfs(pdev);
+
+       /* Limit VFs in case we have more VFs than LBK channels available. */
+       if (vfs > chans)
+               vfs = chans;
+
+       /* AF's VFs work in pairs and talk over consecutive loopback channels.
+        * Thus we want to enable maximum even number of VFs. In case
+        * odd number of VFs are available then the last VF on the list
+        * remains disabled.
+        */
+       if (vfs & 0x1) {
+               dev_warn(&pdev->dev,
+                        "Number of VFs should be even. Enabling %d out of %d.\n",
+                        vfs - 1, vfs);
+               vfs--;
+       }
+
+       if (!vfs)
+               return 0;
+
+       /* Save VFs number for reference in VF interrupts handlers.
+        * Since interrupts might start arriving during SRIOV enablement
+        * ordinary API cannot be used to get number of enabled VFs.
+        */
+       rvu->vfs = vfs;
+
+       err = rvu_mbox_init(rvu, &rvu->afvf_wq_info, TYPE_AFVF, vfs,
+                           rvu_afvf_mbox_handler, rvu_afvf_mbox_up_handler);
+       if (err)
+               return err;
+
+       rvu_enable_afvf_intr(rvu);
+       /* Make sure IRQs are enabled before SRIOV. */
+       mb();
+
+       err = pci_enable_sriov(pdev, vfs);
+       if (err) {
+               rvu_disable_afvf_intr(rvu);
+               rvu_mbox_destroy(&rvu->afvf_wq_info);
+               return err;
+       }
+
+       return 0;
+}
+
+static void rvu_disable_sriov(struct rvu *rvu)
+{
+       rvu_disable_afvf_intr(rvu);
+       rvu_mbox_destroy(&rvu->afvf_wq_info);
+       pci_disable_sriov(rvu->pdev);
+}
+
+static void rvu_update_module_params(struct rvu *rvu)
+{
+       const char *default_pfl_name = "default";
+
+       strscpy(rvu->mkex_pfl_name,
+               mkex_profile ? mkex_profile : default_pfl_name, MKEX_NAME_LEN);
+}
+
 static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct device *dev = &pdev->dev;
@@ -1680,6 +2424,9 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_regions;
        }
 
+       /* Store module params in rvu structure */
+       rvu_update_module_params(rvu);
+
        /* Check which blocks the HW supports */
        rvu_check_block_implemented(rvu);
 
@@ -1689,24 +2436,35 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (err)
                goto err_release_regions;
 
-       err = rvu_mbox_init(rvu);
+       /* Init mailbox btw AF and PFs */
+       err = rvu_mbox_init(rvu, &rvu->afpf_wq_info, TYPE_AFPF,
+                           rvu->hw->total_pfs, rvu_afpf_mbox_handler,
+                           rvu_afpf_mbox_up_handler);
        if (err)
                goto err_hwsetup;
 
-       err = rvu_cgx_probe(rvu);
+       err = rvu_flr_init(rvu);
        if (err)
                goto err_mbox;
 
        err = rvu_register_interrupts(rvu);
        if (err)
-               goto err_cgx;
+               goto err_flr;
+
+       /* Enable AF's VFs (if any) */
+       err = rvu_enable_sriov(rvu);
+       if (err)
+               goto err_irq;
 
        return 0;
-err_cgx:
-       rvu_cgx_wq_destroy(rvu);
+err_irq:
+       rvu_unregister_interrupts(rvu);
+err_flr:
+       rvu_flr_wq_destroy(rvu);
 err_mbox:
-       rvu_mbox_destroy(rvu);
+       rvu_mbox_destroy(&rvu->afpf_wq_info);
 err_hwsetup:
+       rvu_cgx_exit(rvu);
        rvu_reset_all_blocks(rvu);
        rvu_free_hw_resources(rvu);
 err_release_regions:
@@ -1725,8 +2483,10 @@ static void rvu_remove(struct pci_dev *pdev)
        struct rvu *rvu = pci_get_drvdata(pdev);
 
        rvu_unregister_interrupts(rvu);
-       rvu_cgx_wq_destroy(rvu);
-       rvu_mbox_destroy(rvu);
+       rvu_flr_wq_destroy(rvu);
+       rvu_cgx_exit(rvu);
+       rvu_mbox_destroy(&rvu->afpf_wq_info);
+       rvu_disable_sriov(rvu);
        rvu_reset_all_blocks(rvu);
        rvu_free_hw_resources(rvu);
 
index 2c0580c..c9d60b0 100644 (file)
@@ -11,6 +11,7 @@
 #ifndef RVU_H
 #define RVU_H
 
+#include <linux/pci.h>
 #include "rvu_struct.h"
 #include "common.h"
 #include "mbox.h"
@@ -18,6 +19,9 @@
 /* PCI device IDs */
 #define        PCI_DEVID_OCTEONTX2_RVU_AF              0xA065
 
+/* Subsystem Device ID */
+#define PCI_SUBSYS_DEVID_96XX                  0xB200
+
 /* PCI BAR nos */
 #define        PCI_AF_REG_BAR_NUM                      0
 #define        PCI_PF_REG_BAR_NUM                      2
@@ -64,7 +68,7 @@ struct nix_mcast {
        struct qmem     *mcast_buf;
        int             replay_pkind;
        int             next_free_mce;
-       spinlock_t      mce_lock; /* Serialize MCE updates */
+       struct mutex    mce_lock; /* Serialize MCE updates */
 };
 
 struct nix_mce_list {
@@ -74,15 +78,27 @@ struct nix_mce_list {
 };
 
 struct npc_mcam {
-       spinlock_t      lock;   /* MCAM entries and counters update lock */
+       struct rsrc_bmap counters;
+       struct mutex    lock;   /* MCAM entries and counters update lock */
+       unsigned long   *bmap;          /* bitmap, 0 => bmap_entries */
+       unsigned long   *bmap_reverse;  /* Reverse bitmap, bmap_entries => 0 */
+       u16     bmap_entries;   /* Number of unreserved MCAM entries */
+       u16     bmap_fcnt;      /* MCAM entries free count */
+       u16     *entry2pfvf_map;
+       u16     *entry2cntr_map;
+       u16     *cntr2pfvf_map;
+       u16     *cntr_refcnt;
        u8      keysize;        /* MCAM keysize 112/224/448 bits */
        u8      banks;          /* Number of MCAM banks */
        u8      banks_per_entry;/* Number of keywords in key */
        u16     banksize;       /* Number of MCAM entries in each bank */
        u16     total_entries;  /* Total number of MCAM entries */
-       u16     entries;        /* Total minus reserved for NIX LFs */
        u16     nixlf_offset;   /* Offset of nixlf rsvd uncast entries */
        u16     pf_offset;      /* Offset of PF's rsvd bcast, promisc entries */
+       u16     lprio_count;
+       u16     lprio_start;
+       u16     hprio_count;
+       u16     hprio_end;
 };
 
 /* Structure for per RVU func info ie PF/VF */
@@ -122,18 +138,35 @@ struct rvu_pfvf {
        u16             tx_chan_base;
        u8              rx_chan_cnt; /* total number of RX channels */
        u8              tx_chan_cnt; /* total number of TX channels */
+       u16             maxlen;
+       u16             minlen;
 
        u8              mac_addr[ETH_ALEN]; /* MAC address of this PF/VF */
 
        /* Broadcast pkt replication info */
        u16                     bcast_mce_idx;
        struct nix_mce_list     bcast_mce_list;
+
+       /* VLAN offload */
+       struct mcam_entry entry;
+       int rxvlan_index;
+       bool rxvlan;
 };
 
 struct nix_txsch {
        struct rsrc_bmap schq;
        u8   lvl;
-       u16  *pfvf_map;
+#define NIX_TXSCHQ_TL1_CFG_DONE       BIT_ULL(0)
+#define TXSCH_MAP_FUNC(__pfvf_map)    ((__pfvf_map) & 0xFFFF)
+#define TXSCH_MAP_FLAGS(__pfvf_map)   ((__pfvf_map) >> 16)
+#define TXSCH_MAP(__func, __flags)    (((__func) & 0xFFFF) | ((__flags) << 16))
+       u32  *pfvf_map;
+};
+
+struct nix_mark_format {
+       u8 total;
+       u8 in_use;
+       u32 *cfg;
 };
 
 struct npc_pkind {
@@ -141,9 +174,23 @@ struct npc_pkind {
        u32     *pfchan_map;
 };
 
+struct nix_flowkey {
+#define NIX_FLOW_KEY_ALG_MAX 32
+       u32 flowkey[NIX_FLOW_KEY_ALG_MAX];
+       int in_use;
+};
+
+struct nix_lso {
+       u8 total;
+       u8 in_use;
+};
+
 struct nix_hw {
        struct nix_txsch txsch[NIX_TXSCH_LVL_CNT]; /* Tx schedulers */
        struct nix_mcast mcast;
+       struct nix_flowkey flowkey;
+       struct nix_mark_format mark_format;
+       struct nix_lso lso;
 };
 
 struct rvu_hwinfo {
@@ -164,6 +211,16 @@ struct rvu_hwinfo {
        struct npc_mcam  mcam;
 };
 
+struct mbox_wq_info {
+       struct otx2_mbox mbox;
+       struct rvu_work *mbox_wrk;
+
+       struct otx2_mbox mbox_up;
+       struct rvu_work *mbox_wrk_up;
+
+       struct workqueue_struct *mbox_wq;
+};
+
 struct rvu {
        void __iomem            *afreg_base;
        void __iomem            *pfreg_base;
@@ -172,14 +229,17 @@ struct rvu {
        struct rvu_hwinfo       *hw;
        struct rvu_pfvf         *pf;
        struct rvu_pfvf         *hwvf;
-       spinlock_t              rsrc_lock; /* Serialize resource alloc/free */
+       struct mutex            rsrc_lock; /* Serialize resource alloc/free */
+       int                     vfs; /* Number of VFs attached to RVU */
 
        /* Mbox */
-       struct otx2_mbox        mbox;
-       struct rvu_work         *mbox_wrk;
-       struct otx2_mbox        mbox_up;
-       struct rvu_work         *mbox_wrk_up;
-       struct workqueue_struct *mbox_wq;
+       struct mbox_wq_info     afpf_wq_info;
+       struct mbox_wq_info     afvf_wq_info;
+
+       /* PF FLR */
+       struct rvu_work         *flr_wrk;
+       struct workqueue_struct *flr_wq;
+       struct mutex            flr_lock; /* Serialize FLRs */
 
        /* MSI-X */
        u16                     num_vec;
@@ -190,7 +250,7 @@ struct rvu {
        /* CGX */
 #define PF_CGXMAP_BASE         1 /* PF 0 is reserved for RVU PF */
        u8                      cgx_mapped_pfs;
-       u8                      cgx_cnt; /* available cgx ports */
+       u8                      cgx_cnt_max;     /* CGX port count max */
        u8                      *pf2cgxlmac_map; /* pf to cgx_lmac map */
        u16                     *cgxlmac2pf_map; /* bitmap of mapped pfs for
                                                  * every cgx lmac port
@@ -201,6 +261,8 @@ struct rvu {
        struct                  workqueue_struct *cgx_evh_wq;
        spinlock_t              cgx_evq_lock; /* cgx event queue lock */
        struct list_head        cgx_evq_head; /* cgx event queue head */
+
+       char mkex_pfl_name[MKEX_NAME_LEN]; /* Configured MKEX profile name */
 };
 
 static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val)
@@ -223,9 +285,22 @@ static inline u64 rvupf_read64(struct rvu *rvu, u64 offset)
        return readq(rvu->pfreg_base + offset);
 }
 
+static inline bool is_rvu_9xxx_A0(struct rvu *rvu)
+{
+       struct pci_dev *pdev = rvu->pdev;
+
+       return (pdev->revision == 0x00) &&
+               (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX);
+}
+
 /* Function Prototypes
  * RVU
  */
+static inline int is_afvf(u16 pcifunc)
+{
+       return !(pcifunc & ~RVU_PFVF_FUNC_MASK);
+}
+
 int rvu_alloc_bitmap(struct rsrc_bmap *rsrc);
 int rvu_alloc_rsrc(struct rsrc_bmap *rsrc);
 void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id);
@@ -236,6 +311,7 @@ int rvu_get_pf(u16 pcifunc);
 struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc);
 void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf);
 bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr);
+bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype);
 int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot);
 int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf);
 int rvu_get_blkaddr(struct rvu *rvu, int blktype, u16 pcifunc);
@@ -266,89 +342,110 @@ static inline void rvu_get_cgx_lmac_id(u8 map, u8 *cgx_id, u8 *lmac_id)
        *lmac_id = (map & 0xF);
 }
 
-int rvu_cgx_probe(struct rvu *rvu);
-void rvu_cgx_wq_destroy(struct rvu *rvu);
+int rvu_cgx_init(struct rvu *rvu);
+int rvu_cgx_exit(struct rvu *rvu);
 void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu);
 int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start);
-int rvu_mbox_handler_CGX_START_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STOP_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_rxtx(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req,
                               struct cgx_stats_rsp *rsp);
-int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp);
-int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp);
-int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_PROMISC_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_START_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_linkevents(struct rvu *rvu, struct msg_req *req,
                                          struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_STOP_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_linkevents(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_GET_LINKINFO(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req,
                                      struct cgx_link_info_msg *rsp);
-int rvu_mbox_handler_CGX_INTLBK_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_enable(struct rvu *rvu, struct msg_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_CGX_INTLBK_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp);
 
 /* NPA APIs */
 int rvu_npa_init(struct rvu *rvu);
 void rvu_npa_freemem(struct rvu *rvu);
-int rvu_mbox_handler_NPA_AQ_ENQ(struct rvu *rvu,
+void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf);
+int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu,
                                struct npa_aq_enq_req *req,
                                struct npa_aq_enq_rsp *rsp);
-int rvu_mbox_handler_NPA_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_NPA_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu,
                                  struct npa_lf_alloc_req *req,
                                  struct npa_lf_alloc_rsp *rsp);
-int rvu_mbox_handler_NPA_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp);
 
 /* NIX APIs */
+bool is_nixlf_attached(struct rvu *rvu, u16 pcifunc);
 int rvu_nix_init(struct rvu *rvu);
+int rvu_nix_reserve_mark_format(struct rvu *rvu, struct nix_hw *nix_hw,
+                               int blkaddr, u32 cfg);
 void rvu_nix_freemem(struct rvu *rvu);
 int rvu_get_nixlf_count(struct rvu *rvu);
-int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
+void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int npalf);
+int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
                                  struct nix_lf_alloc_req *req,
                                  struct nix_lf_alloc_rsp *rsp);
-int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu,
                                struct nix_aq_enq_req *req,
                                struct nix_aq_enq_rsp *rsp);
-int rvu_mbox_handler_NIX_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                                     struct nix_txsch_alloc_req *req,
                                     struct nix_txsch_alloc_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCH_FREE(struct rvu *rvu,
+int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu,
                                    struct nix_txsch_free_req *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                                    struct nix_txschq_config *req,
                                    struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_VTAG_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu,
                                  struct nix_vtag_config *req,
                                  struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_RSS_FLOWKEY_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req,
+                                     struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu,
                                         struct nix_rss_flowkey_cfg *req,
-                                        struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
+                                        struct nix_rss_flowkey_cfg_rsp *rsp);
+int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu,
                                      struct nix_set_mac_addr *req,
                                      struct msg_rsp *rsp);
-int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
+int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                                     struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
+                                    struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
+                                        struct nix_mark_format_cfg  *req,
+                                        struct nix_mark_format_cfg_rsp *rsp);
+int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
+                                   struct msg_rsp *rsp);
+int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu,
+                                       struct nix_lso_format_cfg *req,
+                                       struct nix_lso_format_cfg_rsp *rsp);
 
 /* NPC APIs */
 int rvu_npc_init(struct rvu *rvu);
@@ -360,9 +457,48 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
 void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                                   int nixlf, u64 chan, bool allmulti);
 void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
                                       int nixlf, u64 chan);
+int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
+void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf);
 void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
                                    int group, int alg_idx, int mcam_index);
+int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu,
+                                         struct npc_mcam_alloc_entry_req *req,
+                                         struct npc_mcam_alloc_entry_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
+                                        struct npc_mcam_free_entry_req *req,
+                                        struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu,
+                                         struct npc_mcam_write_entry_req *req,
+                                         struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_ena_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_dis_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
+                                         struct npc_mcam_shift_entry_req *req,
+                                         struct npc_mcam_shift_entry_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_alloc_counter(struct rvu *rvu,
+                               struct npc_mcam_alloc_counter_req *req,
+                               struct npc_mcam_alloc_counter_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu,
+                  struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_clear_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu,
+               struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_counter_stats(struct rvu *rvu,
+                       struct npc_mcam_oper_counter_req *req,
+                       struct npc_mcam_oper_counter_rsp *rsp);
+int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu,
+                         struct npc_mcam_alloc_and_write_entry_req *req,
+                         struct npc_mcam_alloc_and_write_entry_rsp *rsp);
+int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req,
+                                    struct npc_get_kex_cfg_rsp *rsp);
 #endif /* RVU_H */
index 188185c..7d7133c 100644 (file)
@@ -20,14 +20,14 @@ struct cgx_evq_entry {
        struct cgx_link_event link_event;
 };
 
-#define M(_name, _id, _req_type, _rsp_type)                            \
+#define M(_name, _id, _fn_name, _req_type, _rsp_type)                  \
 static struct _req_type __maybe_unused                                 \
-*otx2_mbox_alloc_msg_ ## _name(struct rvu *rvu, int devid)             \
+*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid)          \
 {                                                                      \
        struct _req_type *req;                                          \
                                                                        \
        req = (struct _req_type *)otx2_mbox_alloc_msg_rsp(              \
-               &rvu->mbox_up, devid, sizeof(struct _req_type),         \
+               &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \
                sizeof(struct _rsp_type));                              \
        if (!req)                                                       \
                return NULL;                                            \
@@ -52,7 +52,7 @@ static inline u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id)
 
 void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu)
 {
-       if (cgx_id >= rvu->cgx_cnt)
+       if (cgx_id >= rvu->cgx_cnt_max)
                return NULL;
 
        return rvu->cgx_idmap[cgx_id];
@@ -61,38 +61,40 @@ void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu)
 static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
-       int cgx_cnt = rvu->cgx_cnt;
+       int cgx_cnt_max = rvu->cgx_cnt_max;
        int cgx, lmac_cnt, lmac;
        int pf = PF_CGXMAP_BASE;
        int size, free_pkind;
 
-       if (!cgx_cnt)
+       if (!cgx_cnt_max)
                return 0;
 
-       if (cgx_cnt > 0xF || MAX_LMAC_PER_CGX > 0xF)
+       if (cgx_cnt_max > 0xF || MAX_LMAC_PER_CGX > 0xF)
                return -EINVAL;
 
        /* Alloc map table
         * An additional entry is required since PF id starts from 1 and
         * hence entry at offset 0 is invalid.
         */
-       size = (cgx_cnt * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
-       rvu->pf2cgxlmac_map = devm_kzalloc(rvu->dev, size, GFP_KERNEL);
+       size = (cgx_cnt_max * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
+       rvu->pf2cgxlmac_map = devm_kmalloc(rvu->dev, size, GFP_KERNEL);
        if (!rvu->pf2cgxlmac_map)
                return -ENOMEM;
 
-       /* Initialize offset 0 with an invalid cgx and lmac id */
-       rvu->pf2cgxlmac_map[0] = 0xFF;
+       /* Initialize all entries with an invalid cgx and lmac id */
+       memset(rvu->pf2cgxlmac_map, 0xFF, size);
 
        /* Reverse map table */
        rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev,
-                                 cgx_cnt * MAX_LMAC_PER_CGX * sizeof(u16),
+                                 cgx_cnt_max * MAX_LMAC_PER_CGX * sizeof(u16),
                                  GFP_KERNEL);
        if (!rvu->cgxlmac2pf_map)
                return -ENOMEM;
 
        rvu->cgx_mapped_pfs = 0;
-       for (cgx = 0; cgx < cgx_cnt; cgx++) {
+       for (cgx = 0; cgx < cgx_cnt_max; cgx++) {
+               if (!rvu_cgx_pdata(cgx, rvu))
+                       continue;
                lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
                for (lmac = 0; lmac < lmac_cnt; lmac++, pf++) {
                        rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac);
@@ -177,12 +179,12 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)
                }
 
                /* Send mbox message to PF */
-               msg = otx2_mbox_alloc_msg_CGX_LINK_EVENT(rvu, pfid);
+               msg = otx2_mbox_alloc_msg_cgx_link_event(rvu, pfid);
                if (!msg)
                        continue;
                msg->link_info = *linfo;
-               otx2_mbox_msg_send(&rvu->mbox_up, pfid);
-               err = otx2_mbox_wait_for_rsp(&rvu->mbox_up, pfid);
+               otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pfid);
+               err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pfid);
                if (err)
                        dev_warn(rvu->dev, "notification to pf %d failed\n",
                                 pfid);
@@ -216,7 +218,7 @@ static void cgx_evhandler_task(struct work_struct *work)
        } while (1);
 }
 
-static void cgx_lmac_event_handler_init(struct rvu *rvu)
+static int cgx_lmac_event_handler_init(struct rvu *rvu)
 {
        struct cgx_event_cb cb;
        int cgx, lmac, err;
@@ -228,14 +230,16 @@ static void cgx_lmac_event_handler_init(struct rvu *rvu)
        rvu->cgx_evh_wq = alloc_workqueue("rvu_evh_wq", 0, 0);
        if (!rvu->cgx_evh_wq) {
                dev_err(rvu->dev, "alloc workqueue failed");
-               return;
+               return -ENOMEM;
        }
 
        cb.notify_link_chg = cgx_lmac_postevent; /* link change call back */
        cb.data = rvu;
 
-       for (cgx = 0; cgx < rvu->cgx_cnt; cgx++) {
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
                cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
                for (lmac = 0; lmac < cgx_get_lmac_cnt(cgxd); lmac++) {
                        err = cgx_lmac_evh_register(&cb, cgxd, lmac);
                        if (err)
@@ -244,9 +248,11 @@ static void cgx_lmac_event_handler_init(struct rvu *rvu)
                                        cgx, lmac);
                }
        }
+
+       return 0;
 }
 
-void rvu_cgx_wq_destroy(struct rvu *rvu)
+static void rvu_cgx_wq_destroy(struct rvu *rvu)
 {
        if (rvu->cgx_evh_wq) {
                flush_workqueue(rvu->cgx_evh_wq);
@@ -255,25 +261,28 @@ void rvu_cgx_wq_destroy(struct rvu *rvu)
        }
 }
 
-int rvu_cgx_probe(struct rvu *rvu)
+int rvu_cgx_init(struct rvu *rvu)
 {
-       int i, err;
+       int cgx, err;
+       void *cgxd;
 
-       /* find available cgx ports */
-       rvu->cgx_cnt = cgx_get_cgx_cnt();
-       if (!rvu->cgx_cnt) {
+       /* CGX port id starts from 0 and are not necessarily contiguous
+        * Hence we allocate resources based on the maximum port id value.
+        */
+       rvu->cgx_cnt_max = cgx_get_cgxcnt_max();
+       if (!rvu->cgx_cnt_max) {
                dev_info(rvu->dev, "No CGX devices found!\n");
                return -ENODEV;
        }
 
-       rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt * sizeof(void *),
-                                     GFP_KERNEL);
+       rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt_max *
+                                     sizeof(void *), GFP_KERNEL);
        if (!rvu->cgx_idmap)
                return -ENOMEM;
 
        /* Initialize the cgxdata table */
-       for (i = 0; i < rvu->cgx_cnt; i++)
-               rvu->cgx_idmap[i] = cgx_get_pdata(i);
+       for (cgx = 0; cgx < rvu->cgx_cnt_max; cgx++)
+               rvu->cgx_idmap[cgx] = cgx_get_pdata(cgx);
 
        /* Map CGX LMAC interfaces to RVU PFs */
        err = rvu_map_cgx_lmac_pf(rvu);
@@ -281,7 +290,47 @@ int rvu_cgx_probe(struct rvu *rvu)
                return err;
 
        /* Register for CGX events */
-       cgx_lmac_event_handler_init(rvu);
+       err = cgx_lmac_event_handler_init(rvu);
+       if (err)
+               return err;
+
+       /* Ensure event handler registration is completed, before
+        * we turn on the links
+        */
+       mb();
+
+       /* Do link up for all CGX ports */
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
+               cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
+               err = cgx_lmac_linkup_start(cgxd);
+               if (err)
+                       dev_err(rvu->dev,
+                               "Link up process failed to start on cgx %d\n",
+                               cgx);
+       }
+
+       return 0;
+}
+
+int rvu_cgx_exit(struct rvu *rvu)
+{
+       int cgx, lmac;
+       void *cgxd;
+
+       for (cgx = 0; cgx <= rvu->cgx_cnt_max; cgx++) {
+               cgxd = rvu_cgx_pdata(cgx, rvu);
+               if (!cgxd)
+                       continue;
+               for (lmac = 0; lmac < cgx_get_lmac_cnt(cgxd); lmac++)
+                       cgx_lmac_evh_unregister(cgxd, lmac);
+       }
+
+       /* Ensure event handler unregister is completed */
+       mb();
+
+       rvu_cgx_wq_destroy(rvu);
        return 0;
 }
 
@@ -303,21 +352,21 @@ int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start)
        return 0;
 }
 
-int rvu_mbox_handler_CGX_START_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_rxtx(struct rvu *rvu, struct msg_req *req,
                                    struct msg_rsp *rsp)
 {
        rvu_cgx_config_rxtx(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STOP_RXTX(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_rxtx(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp)
 {
        rvu_cgx_config_rxtx(rvu, req->hdr.pcifunc, false);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stats(struct rvu *rvu, struct msg_req *req,
                               struct cgx_stats_rsp *rsp)
 {
        int pf = rvu_get_pf(req->hdr.pcifunc);
@@ -354,7 +403,7 @@ int rvu_mbox_handler_CGX_STATS(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp)
 {
@@ -368,7 +417,7 @@ int rvu_mbox_handler_CGX_MAC_ADDR_SET(struct rvu *rvu,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
+int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu,
                                      struct cgx_mac_addr_set_or_get *req,
                                      struct cgx_mac_addr_set_or_get *rsp)
 {
@@ -387,7 +436,7 @@ int rvu_mbox_handler_CGX_MAC_ADDR_GET(struct rvu *rvu,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
@@ -407,7 +456,7 @@ int rvu_mbox_handler_CGX_PROMISC_ENABLE(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
-int rvu_mbox_handler_CGX_PROMISC_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp)
 {
        u16 pcifunc = req->hdr.pcifunc;
@@ -451,21 +500,21 @@ static int rvu_cgx_config_linkevents(struct rvu *rvu, u16 pcifunc, bool en)
        return 0;
 }
 
-int rvu_mbox_handler_CGX_START_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_start_linkevents(struct rvu *rvu, struct msg_req *req,
                                          struct msg_rsp *rsp)
 {
        rvu_cgx_config_linkevents(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_STOP_LINKEVENTS(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_stop_linkevents(struct rvu *rvu, struct msg_req *req,
                                         struct msg_rsp *rsp)
 {
        rvu_cgx_config_linkevents(rvu, req->hdr.pcifunc, false);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_GET_LINKINFO(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_get_linkinfo(struct rvu *rvu, struct msg_req *req,
                                      struct cgx_link_info_msg *rsp)
 {
        u8 cgx_id, lmac_id;
@@ -500,14 +549,14 @@ static int rvu_cgx_config_intlbk(struct rvu *rvu, u16 pcifunc, bool en)
                                          lmac_id, en);
 }
 
-int rvu_mbox_handler_CGX_INTLBK_ENABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_enable(struct rvu *rvu, struct msg_req *req,
                                       struct msg_rsp *rsp)
 {
        rvu_cgx_config_intlbk(rvu, req->hdr.pcifunc, true);
        return 0;
 }
 
-int rvu_mbox_handler_CGX_INTLBK_DISABLE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_cgx_intlbk_disable(struct rvu *rvu, struct msg_req *req,
                                        struct msg_rsp *rsp)
 {
        rvu_cgx_config_intlbk(rvu, req->hdr.pcifunc, false);
index a5ab7ef..4a7609f 100644 (file)
@@ -43,6 +43,19 @@ enum mc_buf_cnt {
        MC_BUF_CNT_2048,
 };
 
+enum nix_makr_fmt_indexes {
+       NIX_MARK_CFG_IP_DSCP_RED,
+       NIX_MARK_CFG_IP_DSCP_YELLOW,
+       NIX_MARK_CFG_IP_DSCP_YELLOW_RED,
+       NIX_MARK_CFG_IP_ECN_RED,
+       NIX_MARK_CFG_IP_ECN_YELLOW,
+       NIX_MARK_CFG_IP_ECN_YELLOW_RED,
+       NIX_MARK_CFG_VLAN_DEI_RED,
+       NIX_MARK_CFG_VLAN_DEI_YELLOW,
+       NIX_MARK_CFG_VLAN_DEI_YELLOW_RED,
+       NIX_MARK_CFG_MAX,
+};
+
 /* For now considering MC resources needed for broadcast
  * pkt replication only. i.e 256 HWVFs + 12 PFs.
  */
@@ -55,6 +68,17 @@ struct mce {
        u16                     pcifunc;
 };
 
+bool is_nixlf_attached(struct rvu *rvu, u16 pcifunc)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return false;
+       return true;
+}
+
 int rvu_get_nixlf_count(struct rvu *rvu)
 {
        struct rvu_block *block;
@@ -94,11 +118,29 @@ static inline struct nix_hw *get_nix_hw(struct rvu_hwinfo *hw, int blkaddr)
        return NULL;
 }
 
+static void nix_rx_sync(struct rvu *rvu, int blkaddr)
+{
+       int err;
+
+       /*Sync all in flight RX packets to LLC/DRAM */
+       rvu_write64(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0));
+       err = rvu_poll_reg(rvu, blkaddr, NIX_AF_RX_SW_SYNC, BIT_ULL(0), true);
+       if (err)
+               dev_err(rvu->dev, "NIX RX software sync failed\n");
+
+       /* As per a HW errata in 9xxx A0 silicon, HW may clear SW_SYNC[ENA]
+        * bit too early. Hence wait for 50us more.
+        */
+       if (is_rvu_9xxx_A0(rvu))
+               usleep_range(50, 60);
+}
+
 static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
                            int lvl, u16 pcifunc, u16 schq)
 {
        struct nix_txsch *txsch;
        struct nix_hw *nix_hw;
+       u16 map_func;
 
        nix_hw = get_nix_hw(rvu->hw, blkaddr);
        if (!nix_hw)
@@ -109,12 +151,19 @@ static bool is_valid_txschq(struct rvu *rvu, int blkaddr,
        if (schq >= txsch->schq.max)
                return false;
 
-       spin_lock(&rvu->rsrc_lock);
-       if (txsch->pfvf_map[schq] != pcifunc) {
-               spin_unlock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
+       map_func = TXSCH_MAP_FUNC(txsch->pfvf_map[schq]);
+       mutex_unlock(&rvu->rsrc_lock);
+
+       /* For TL1 schq, sharing across VF's of same PF is ok */
+       if (lvl == NIX_TXSCH_LVL_TL1 &&
+           rvu_get_pf(map_func) != rvu_get_pf(pcifunc))
                return false;
-       }
-       spin_unlock(&rvu->rsrc_lock);
+
+       if (lvl != NIX_TXSCH_LVL_TL1 &&
+           map_func != pcifunc)
+               return false;
+
        return true;
 }
 
@@ -122,7 +171,7 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
 {
        struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        u8 cgx_id, lmac_id;
-       int pkind, pf;
+       int pkind, pf, vf;
        int err;
 
        pf = rvu_get_pf(pcifunc);
@@ -148,6 +197,14 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
                rvu_npc_set_pkind(rvu, pkind, pfvf);
                break;
        case NIX_INTF_TYPE_LBK:
+               vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
+               pfvf->rx_chan_base = NIX_CHAN_LBK_CHX(0, vf);
+               pfvf->tx_chan_base = vf & 0x1 ? NIX_CHAN_LBK_CHX(0, vf - 1) :
+                                               NIX_CHAN_LBK_CHX(0, vf + 1);
+               pfvf->rx_chan_cnt = 1;
+               pfvf->tx_chan_cnt = 1;
+               rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
+                                             pfvf->rx_chan_base, false);
                break;
        }
 
@@ -168,14 +225,21 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf)
 
        rvu_npc_install_bcast_match_entry(rvu, pcifunc,
                                          nixlf, pfvf->rx_chan_base);
+       pfvf->maxlen = NIC_HW_MIN_FRS;
+       pfvf->minlen = NIC_HW_MIN_FRS;
 
        return 0;
 }
 
 static void nix_interface_deinit(struct rvu *rvu, u16 pcifunc, u8 nixlf)
 {
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        int err;
 
+       pfvf->maxlen = 0;
+       pfvf->minlen = 0;
+       pfvf->rxvlan = false;
+
        /* Remove this PF_FUNC from bcast pkt replication list */
        err = nix_update_bcast_mce_list(rvu, pcifunc, false);
        if (err) {
@@ -234,17 +298,21 @@ static void nix_setup_lso_tso_l4(struct rvu *rvu, int blkaddr,
        /* TCP's flags field */
        field.layer = NIX_TXLAYER_OL4;
        field.offset = 12;
-       field.sizem1 = 0; /* not needed */
+       field.sizem1 = 1; /* 2 bytes */
        field.alg = NIX_LSOALG_TCP_FLAGS;
        rvu_write64(rvu, blkaddr,
                    NIX_AF_LSO_FORMATX_FIELDX(format, (*fidx)++),
                    *(u64 *)&field);
 }
 
-static void nix_setup_lso(struct rvu *rvu, int blkaddr)
+static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
 {
        u64 cfg, idx, fidx = 0;
 
+       /* Get max HW supported format indices */
+       cfg = (rvu_read64(rvu, blkaddr, NIX_AF_CONST1) >> 48) & 0xFF;
+       nix_hw->lso.total = cfg;
+
        /* Enable LSO */
        cfg = rvu_read64(rvu, blkaddr, NIX_AF_LSO_CFG);
        /* For TSO, set first and middle segment flags to
@@ -254,7 +322,10 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
        cfg |= (0xFFF2ULL << 32) | (0xFFF2ULL << 16);
        rvu_write64(rvu, blkaddr, NIX_AF_LSO_CFG, cfg | BIT_ULL(63));
 
-       /* Configure format fields for TCPv4 segmentation offload */
+       /* Setup default static LSO formats
+        *
+        * Configure format fields for TCPv4 segmentation offload
+        */
        idx = NIX_LSO_FORMAT_IDX_TSOV4;
        nix_setup_lso_tso_l3(rvu, blkaddr, idx, true, &fidx);
        nix_setup_lso_tso_l4(rvu, blkaddr, idx, &fidx);
@@ -264,6 +335,7 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
                rvu_write64(rvu, blkaddr,
                            NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
        }
+       nix_hw->lso.in_use++;
 
        /* Configure format fields for TCPv6 segmentation offload */
        idx = NIX_LSO_FORMAT_IDX_TSOV6;
@@ -276,6 +348,7 @@ static void nix_setup_lso(struct rvu *rvu, int blkaddr)
                rvu_write64(rvu, blkaddr,
                            NIX_AF_LSO_FORMATX_FIELDX(idx, fidx), 0x0ULL);
        }
+       nix_hw->lso.in_use++;
 }
 
 static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
@@ -388,9 +461,8 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
        bool ena;
        u64 cfg;
 
-       pfvf = rvu_get_pfvf(rvu, pcifunc);
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
-       if (!pfvf->nixlf || blkaddr < 0)
+       if (blkaddr < 0)
                return NIX_AF_ERR_AF_LF_INVALID;
 
        block = &hw->block[blkaddr];
@@ -400,9 +472,14 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
                return NIX_AF_ERR_AQ_ENQUEUE;
        }
 
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
        nixlf = rvu_get_lf(rvu, block, pcifunc, 0);
-       if (nixlf < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
+
+       /* Skip NIXLF check for broadcast MCE entry init */
+       if (!(!rsp && req->ctype == NIX_AQ_CTYPE_MCE)) {
+               if (!pfvf->nixlf || nixlf < 0)
+                       return NIX_AF_ERR_AF_LF_INVALID;
+       }
 
        switch (req->ctype) {
        case NIX_AQ_CTYPE_RQ:
@@ -447,7 +524,9 @@ static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
 
        /* Check if SQ pointed SMQ belongs to this PF/VF or not */
        if (req->ctype == NIX_AQ_CTYPE_SQ &&
-           req->op != NIX_AQ_INSTOP_WRITE) {
+           ((req->op == NIX_AQ_INSTOP_INIT && req->sq.ena) ||
+            (req->op == NIX_AQ_INSTOP_WRITE &&
+             req->sq_mask.ena && req->sq_mask.smq && req->sq.ena))) {
                if (!is_valid_txschq(rvu, blkaddr, NIX_TXSCH_LVL_SMQ,
                                     pcifunc, req->sq.smq))
                        return NIX_AF_ERR_AQ_ENQUEUE;
@@ -637,25 +716,25 @@ static int nix_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req)
        return err;
 }
 
-int rvu_mbox_handler_NIX_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_nix_aq_enq(struct rvu *rvu,
                                struct nix_aq_enq_req *req,
                                struct nix_aq_enq_rsp *rsp)
 {
        return rvu_nix_aq_enq_inst(rvu, req, rsp);
 }
 
-int rvu_mbox_handler_NIX_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_nix_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp)
 {
        return nix_lf_hwctx_disable(rvu, req);
 }
 
-int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
                                  struct nix_lf_alloc_req *req,
                                  struct nix_lf_alloc_rsp *rsp)
 {
-       int nixlf, qints, hwctx_size, err, rc = 0;
+       int nixlf, qints, hwctx_size, intf, err, rc = 0;
        struct rvu_hwinfo *hw = rvu->hw;
        u16 pcifunc = req->hdr.pcifunc;
        struct rvu_block *block;
@@ -676,6 +755,24 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
        if (nixlf < 0)
                return NIX_AF_ERR_AF_LF_INVALID;
 
+       /* Check if requested 'NIXLF <=> NPALF' mapping is valid */
+       if (req->npa_func) {
+               /* If default, use 'this' NIXLF's PFFUNC */
+               if (req->npa_func == RVU_DEFAULT_PF_FUNC)
+                       req->npa_func = pcifunc;
+               if (!is_pffunc_map_valid(rvu, req->npa_func, BLKTYPE_NPA))
+                       return NIX_AF_INVAL_NPA_PF_FUNC;
+       }
+
+       /* Check if requested 'NIXLF <=> SSOLF' mapping is valid */
+       if (req->sso_func) {
+               /* If default, use 'this' NIXLF's PFFUNC */
+               if (req->sso_func == RVU_DEFAULT_PF_FUNC)
+                       req->sso_func = pcifunc;
+               if (!is_pffunc_map_valid(rvu, req->sso_func, BLKTYPE_SSO))
+                       return NIX_AF_INVAL_SSO_PF_FUNC;
+       }
+
        /* If RSS is being enabled, check if requested config is valid.
         * RSS table size should be power of two, otherwise
         * RSS_GRP::OFFSET + adder might go beyond that group or
@@ -777,21 +874,20 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
                    (u64)pfvf->nix_qints_ctx->iova);
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_CFG(nixlf), BIT_ULL(36));
 
+       /* Setup VLANX TPID's.
+        * Use VLAN1 for 802.1Q
+        * and VLAN0 for 802.1AD.
+        */
+       cfg = (0x8100ULL << 16) | 0x88A8ULL;
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG(nixlf), cfg);
+
        /* Enable LMTST for this NIX LF */
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_CFG2(nixlf), BIT_ULL(0));
 
-       /* Set CQE/WQE size, NPA_PF_FUNC for SQBs and also SSO_PF_FUNC
-        * If requester has sent a 'RVU_DEFAULT_PF_FUNC' use this NIX LF's
-        * PCIFUNC itself.
-        */
-       if (req->npa_func == RVU_DEFAULT_PF_FUNC)
-               cfg = pcifunc;
-       else
+       /* Set CQE/WQE size, NPA_PF_FUNC for SQBs and also SSO_PF_FUNC */
+       if (req->npa_func)
                cfg = req->npa_func;
-
-       if (req->sso_func == RVU_DEFAULT_PF_FUNC)
-               cfg |= (u64)pcifunc << 16;
-       else
+       if (req->sso_func)
                cfg |= (u64)req->sso_func << 16;
 
        cfg |= (u64)req->xqe_sz << 33;
@@ -800,10 +896,14 @@ int rvu_mbox_handler_NIX_LF_ALLOC(struct rvu *rvu,
        /* Config Rx pkt length, csum checks and apad  enable / disable */
        rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg);
 
-       err = nix_interface_init(rvu, pcifunc, NIX_INTF_TYPE_CGX, nixlf);
+       intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX;
+       err = nix_interface_init(rvu, pcifunc, intf, nixlf);
        if (err)
                goto free_mem;
 
+       /* Disable NPC entries as NIXLF's contexts are not initialized yet */
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
+
        goto exit;
 
 free_mem:
@@ -823,10 +923,18 @@ exit:
        rsp->tx_chan_cnt = pfvf->tx_chan_cnt;
        rsp->lso_tsov4_idx = NIX_LSO_FORMAT_IDX_TSOV4;
        rsp->lso_tsov6_idx = NIX_LSO_FORMAT_IDX_TSOV6;
+       /* Get HW supported stat count */
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST1);
+       rsp->lf_rx_stats = ((cfg >> 32) & 0xFF);
+       rsp->lf_tx_stats = ((cfg >> 24) & 0xFF);
+       /* Get count of CQ IRQs and error IRQs supported per LF */
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
+       rsp->qints = ((cfg >> 12) & 0xFFF);
+       rsp->cints = ((cfg >> 24) & 0xFFF);
        return rc;
 }
 
-int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -860,6 +968,41 @@ int rvu_mbox_handler_NIX_LF_FREE(struct rvu *rvu, struct msg_req *req,
        return 0;
 }
 
+int rvu_mbox_handler_nix_mark_format_cfg(struct rvu *rvu,
+                                        struct nix_mark_format_cfg  *req,
+                                        struct nix_mark_format_cfg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       int blkaddr, rc;
+       u32 cfg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       cfg = (((u32)req->offset & 0x7) << 16) |
+             (((u32)req->y_mask & 0xF) << 12) |
+             (((u32)req->y_val & 0xF) << 8) |
+             (((u32)req->r_mask & 0xF) << 4) | ((u32)req->r_val & 0xF);
+
+       rc = rvu_nix_reserve_mark_format(rvu, nix_hw, blkaddr, cfg);
+       if (rc < 0) {
+               dev_err(rvu->dev, "No mark_format_ctl for (pf:%d, vf:%d)",
+                       rvu_get_pf(pcifunc), pcifunc & RVU_PFVF_FUNC_MASK);
+               return NIX_AF_ERR_MARK_CFG_FAIL;
+       }
+
+       rsp->mark_format_idx = rc;
+       return 0;
+}
+
 /* Disable shaping of pkts by a scheduler queue
  * at a given scheduler level.
  */
@@ -918,7 +1061,74 @@ static void nix_reset_tx_linkcfg(struct rvu *rvu, int blkaddr,
                            NIX_AF_TL3_TL2X_LINKX_CFG(schq, link), 0x00);
 }
 
-int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
+static int
+rvu_get_tl1_schqs(struct rvu *rvu, int blkaddr, u16 pcifunc,
+                 u16 *schq_list, u16 *schq_cnt)
+{
+       struct nix_txsch *txsch;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       u8 cgx_id, lmac_id;
+       u16 schq_base;
+       u32 *pfvf_map;
+       int pf, intf;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -ENODEV;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1];
+       pfvf_map = txsch->pfvf_map;
+       pf = rvu_get_pf(pcifunc);
+
+       /* static allocation as two TL1's per link */
+       intf = is_afvf(pcifunc) ? NIX_INTF_TYPE_LBK : NIX_INTF_TYPE_CGX;
+
+       switch (intf) {
+       case NIX_INTF_TYPE_CGX:
+               rvu_get_cgx_lmac_id(pfvf->cgx_lmac, &cgx_id, &lmac_id);
+               schq_base = (cgx_id * MAX_LMAC_PER_CGX + lmac_id) * 2;
+               break;
+       case NIX_INTF_TYPE_LBK:
+               schq_base = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX * 2;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       if (schq_base + 1 > txsch->schq.max)
+               return -ENODEV;
+
+       /* init pfvf_map as we store flags */
+       if (pfvf_map[schq_base] == U32_MAX) {
+               pfvf_map[schq_base] =
+                       TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0);
+               pfvf_map[schq_base + 1] =
+                       TXSCH_MAP((pf << RVU_PFVF_PF_SHIFT), 0);
+
+               /* Onetime reset for TL1 */
+               nix_reset_tx_linkcfg(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base);
+               nix_reset_tx_shaping(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base);
+
+               nix_reset_tx_linkcfg(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base + 1);
+               nix_reset_tx_shaping(rvu, blkaddr,
+                                    NIX_TXSCH_LVL_TL1, schq_base + 1);
+       }
+
+       if (schq_list && schq_cnt) {
+               schq_list[0] = schq_base;
+               schq_list[1] = schq_base + 1;
+               *schq_cnt = 2;
+       }
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
                                     struct nix_txsch_alloc_req *req,
                                     struct nix_txsch_alloc_rsp *rsp)
 {
@@ -928,6 +1138,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        struct rvu_pfvf *pfvf;
        struct nix_hw *nix_hw;
        int blkaddr, rc = 0;
+       u32 *pfvf_map;
        u16 schq;
 
        pfvf = rvu_get_pfvf(rvu, pcifunc);
@@ -939,17 +1150,27 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        if (!nix_hw)
                return -EINVAL;
 
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                txsch = &nix_hw->txsch[lvl];
                req_schq = req->schq_contig[lvl] + req->schq[lvl];
+               pfvf_map = txsch->pfvf_map;
+
+               if (!req_schq)
+                       continue;
 
                /* There are only 28 TL1s */
-               if (lvl == NIX_TXSCH_LVL_TL1 && req_schq > txsch->schq.max)
-                       goto err;
+               if (lvl == NIX_TXSCH_LVL_TL1) {
+                       if (req->schq_contig[lvl] ||
+                           req->schq[lvl] > 2 ||
+                           rvu_get_tl1_schqs(rvu, blkaddr,
+                                             pcifunc, NULL, NULL))
+                               goto err;
+                       continue;
+               }
 
                /* Check if request is valid */
-               if (!req_schq || req_schq > MAX_TXSCHQ_PER_FUNC)
+               if (req_schq > MAX_TXSCHQ_PER_FUNC)
                        goto err;
 
                /* If contiguous queues are needed, check for availability */
@@ -965,16 +1186,32 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                txsch = &nix_hw->txsch[lvl];
                rsp->schq_contig[lvl] = req->schq_contig[lvl];
+               pfvf_map = txsch->pfvf_map;
                rsp->schq[lvl] = req->schq[lvl];
 
-               schq = 0;
+               if (!req->schq[lvl] && !req->schq_contig[lvl])
+                       continue;
+
+               /* Handle TL1 specially as it is
+                * allocation is restricted to 2 TL1's
+                * per link
+                */
+
+               if (lvl == NIX_TXSCH_LVL_TL1) {
+                       rsp->schq_contig[lvl] = 0;
+                       rvu_get_tl1_schqs(rvu, blkaddr, pcifunc,
+                                         &rsp->schq_list[lvl][0],
+                                         &rsp->schq[lvl]);
+                       continue;
+               }
+
                /* Alloc contiguous queues first */
                if (req->schq_contig[lvl]) {
                        schq = rvu_alloc_rsrc_contig(&txsch->schq,
                                                     req->schq_contig[lvl]);
 
                        for (idx = 0; idx < req->schq_contig[lvl]; idx++) {
-                               txsch->pfvf_map[schq] = pcifunc;
+                               pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                                nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                                nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
                                rsp->schq_contig_list[lvl][idx] = schq;
@@ -985,7 +1222,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
                /* Alloc non-contiguous queues */
                for (idx = 0; idx < req->schq[lvl]; idx++) {
                        schq = rvu_alloc_rsrc(&txsch->schq);
-                       txsch->pfvf_map[schq] = pcifunc;
+                       pfvf_map[schq] = TXSCH_MAP(pcifunc, 0);
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                        nix_reset_tx_shaping(rvu, blkaddr, lvl, schq);
                        rsp->schq_list[lvl][idx] = schq;
@@ -995,7 +1232,7 @@ int rvu_mbox_handler_NIX_TXSCH_ALLOC(struct rvu *rvu,
 err:
        rc = NIX_AF_ERR_TLX_ALLOC_FAIL;
 exit:
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
        return rc;
 }
 
@@ -1020,14 +1257,14 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
                return NIX_AF_ERR_AF_LF_INVALID;
 
        /* Disable TL2/3 queue links before SMQ flush*/
-       spin_lock(&rvu->rsrc_lock);
+       mutex_lock(&rvu->rsrc_lock);
        for (lvl = NIX_TXSCH_LVL_TL4; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
                if (lvl != NIX_TXSCH_LVL_TL2 && lvl != NIX_TXSCH_LVL_TL4)
                        continue;
 
                txsch = &nix_hw->txsch[lvl];
                for (schq = 0; schq < txsch->schq.max; schq++) {
-                       if (txsch->pfvf_map[schq] != pcifunc)
+                       if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                                continue;
                        nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq);
                }
@@ -1036,7 +1273,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        /* Flush SMQs */
        txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
        for (schq = 0; schq < txsch->schq.max; schq++) {
-               if (txsch->pfvf_map[schq] != pcifunc)
+               if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                        continue;
                cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
                /* Do SMQ flush and set enqueue xoff */
@@ -1054,15 +1291,21 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
 
        /* Now free scheduler queues to free pool */
        for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
+               /* Free all SCHQ's except TL1 as
+                * TL1 is shared across all VF's for a RVU PF
+                */
+               if (lvl == NIX_TXSCH_LVL_TL1)
+                       continue;
+
                txsch = &nix_hw->txsch[lvl];
                for (schq = 0; schq < txsch->schq.max; schq++) {
-                       if (txsch->pfvf_map[schq] != pcifunc)
+                       if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
                                continue;
                        rvu_free_rsrc(&txsch->schq, schq);
                        txsch->pfvf_map[schq] = 0;
                }
        }
-       spin_unlock(&rvu->rsrc_lock);
+       mutex_unlock(&rvu->rsrc_lock);
 
        /* Sync cached info for this LF in NDC-TX to LLC/DRAM */
        rvu_write64(rvu, blkaddr, NIX_AF_NDC_TX_SYNC, BIT_ULL(12) | nixlf);
@@ -1073,11 +1316,81 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
        return 0;
 }
 
-int rvu_mbox_handler_NIX_TXSCH_FREE(struct rvu *rvu,
+static int nix_txschq_free_one(struct rvu *rvu,
+                              struct nix_txsch_free_req *req)
+{
+       int lvl, schq, nixlf, blkaddr, rc;
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_txsch *txsch;
+       struct nix_hw *nix_hw;
+       u32 *pfvf_map;
+       u64 cfg;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       lvl = req->schq_lvl;
+       schq = req->schq;
+       txsch = &nix_hw->txsch[lvl];
+
+       /* Don't allow freeing TL1 */
+       if (lvl > NIX_TXSCH_LVL_TL2 ||
+           schq >= txsch->schq.max)
+               goto err;
+
+       pfvf_map = txsch->pfvf_map;
+       mutex_lock(&rvu->rsrc_lock);
+
+       if (TXSCH_MAP_FUNC(pfvf_map[schq]) != pcifunc) {
+               mutex_unlock(&rvu->rsrc_lock);
+               goto err;
+       }
+
+       /* Flush if it is a SMQ. Onus of disabling
+        * TL2/3 queue links before SMQ flush is on user
+        */
+       if (lvl == NIX_TXSCH_LVL_SMQ) {
+               cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
+               /* Do SMQ flush and set enqueue xoff */
+               cfg |= BIT_ULL(50) | BIT_ULL(49);
+               rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg);
+
+               /* Wait for flush to complete */
+               rc = rvu_poll_reg(rvu, blkaddr,
+                                 NIX_AF_SMQX_CFG(schq), BIT_ULL(49), true);
+               if (rc) {
+                       dev_err(rvu->dev,
+                               "NIXLF%d: SMQ%d flush failed\n", nixlf, schq);
+               }
+       }
+
+       /* Free the resource */
+       rvu_free_rsrc(&txsch->schq, schq);
+       txsch->pfvf_map[schq] = 0;
+       mutex_unlock(&rvu->rsrc_lock);
+       return 0;
+err:
+       return NIX_AF_ERR_TLX_INVALID;
+}
+
+int rvu_mbox_handler_nix_txsch_free(struct rvu *rvu,
                                    struct nix_txsch_free_req *req,
                                    struct msg_rsp *rsp)
 {
-       return nix_txschq_free(rvu, req->hdr.pcifunc);
+       if (req->flags & TXSCHQ_FREE_ALL)
+               return nix_txschq_free(rvu, req->hdr.pcifunc);
+       else
+               return nix_txschq_free_one(rvu, req);
 }
 
 static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr,
@@ -1118,16 +1431,73 @@ static bool is_txschq_config_valid(struct rvu *rvu, u16 pcifunc, int blkaddr,
        return true;
 }
 
-int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
+static int
+nix_tl1_default_cfg(struct rvu *rvu, u16 pcifunc)
+{
+       u16 schq_list[2], schq_cnt, schq;
+       int blkaddr, idx, err = 0;
+       u16 map_func, map_flags;
+       struct nix_hw *nix_hw;
+       u64 reg, regval;
+       u32 *pfvf_map;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       pfvf_map = nix_hw->txsch[NIX_TXSCH_LVL_TL1].pfvf_map;
+
+       mutex_lock(&rvu->rsrc_lock);
+
+       err = rvu_get_tl1_schqs(rvu, blkaddr,
+                               pcifunc, schq_list, &schq_cnt);
+       if (err)
+               goto unlock;
+
+       for (idx = 0; idx < schq_cnt; idx++) {
+               schq = schq_list[idx];
+               map_func = TXSCH_MAP_FUNC(pfvf_map[schq]);
+               map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]);
+
+               /* check if config is already done or this is pf */
+               if (map_flags & NIX_TXSCHQ_TL1_CFG_DONE)
+                       continue;
+
+               /* default configuration */
+               reg = NIX_AF_TL1X_TOPOLOGY(schq);
+               regval = (TXSCH_TL1_DFLT_RR_PRIO << 1);
+               rvu_write64(rvu, blkaddr, reg, regval);
+               reg = NIX_AF_TL1X_SCHEDULE(schq);
+               regval = TXSCH_TL1_DFLT_RR_QTM;
+               rvu_write64(rvu, blkaddr, reg, regval);
+               reg = NIX_AF_TL1X_CIR(schq);
+               regval = 0;
+               rvu_write64(rvu, blkaddr, reg, regval);
+
+               map_flags |= NIX_TXSCHQ_TL1_CFG_DONE;
+               pfvf_map[schq] = TXSCH_MAP(map_func, map_flags);
+       }
+unlock:
+       mutex_unlock(&rvu->rsrc_lock);
+       return err;
+}
+
+int rvu_mbox_handler_nix_txschq_cfg(struct rvu *rvu,
                                    struct nix_txschq_config *req,
                                    struct msg_rsp *rsp)
 {
+       u16 schq, pcifunc = req->hdr.pcifunc;
        struct rvu_hwinfo *hw = rvu->hw;
-       u16 pcifunc = req->hdr.pcifunc;
        u64 reg, regval, schq_regbase;
        struct nix_txsch *txsch;
+       u16 map_func, map_flags;
        struct nix_hw *nix_hw;
        int blkaddr, idx, err;
+       u32 *pfvf_map;
        int nixlf;
 
        if (req->lvl >= NIX_TXSCH_LVL_CNT ||
@@ -1147,6 +1517,16 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
                return NIX_AF_ERR_AF_LF_INVALID;
 
        txsch = &nix_hw->txsch[req->lvl];
+       pfvf_map = txsch->pfvf_map;
+
+       /* VF is only allowed to trigger
+        * setting default cfg on TL1
+        */
+       if (pcifunc & RVU_PFVF_FUNC_MASK &&
+           req->lvl == NIX_TXSCH_LVL_TL1) {
+               return nix_tl1_default_cfg(rvu, pcifunc);
+       }
+
        for (idx = 0; idx < req->num_regs; idx++) {
                reg = req->reg[idx];
                regval = req->regval[idx];
@@ -1164,6 +1544,21 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
                        regval |= ((u64)nixlf << 24);
                }
 
+               /* Mark config as done for TL1 by PF */
+               if (schq_regbase >= NIX_AF_TL1X_SCHEDULE(0) &&
+                   schq_regbase <= NIX_AF_TL1X_GREEN_BYTES(0)) {
+                       schq = TXSCHQ_IDX(reg, TXSCHQ_IDX_SHIFT);
+
+                       mutex_lock(&rvu->rsrc_lock);
+
+                       map_func = TXSCH_MAP_FUNC(pfvf_map[schq]);
+                       map_flags = TXSCH_MAP_FLAGS(pfvf_map[schq]);
+
+                       map_flags |= NIX_TXSCHQ_TL1_CFG_DONE;
+                       pfvf_map[schq] = TXSCH_MAP(map_func, map_flags);
+                       mutex_unlock(&rvu->rsrc_lock);
+               }
+
                rvu_write64(rvu, blkaddr, reg, regval);
 
                /* Check for SMQ flush, if so, poll for its completion */
@@ -1181,35 +1576,22 @@ int rvu_mbox_handler_NIX_TXSCHQ_CFG(struct rvu *rvu,
 static int nix_rx_vtag_cfg(struct rvu *rvu, int nixlf, int blkaddr,
                           struct nix_vtag_config *req)
 {
-       u64 regval = 0;
+       u64 regval = req->vtag_size;
 
-#define NIX_VTAGTYPE_MAX 0x8ull
-#define NIX_VTAGSIZE_MASK 0x7ull
-#define NIX_VTAGSTRIP_CAP_MASK 0x30ull
-
-       if (req->rx.vtag_type >= NIX_VTAGTYPE_MAX ||
-           req->vtag_size > VTAGSIZE_T8)
+       if (req->rx.vtag_type > 7 || req->vtag_size > VTAGSIZE_T8)
                return -EINVAL;
 
-       regval = rvu_read64(rvu, blkaddr,
-                           NIX_AF_LFX_RX_VTAG_TYPEX(nixlf, req->rx.vtag_type));
-
-       if (req->rx.strip_vtag && req->rx.capture_vtag)
-               regval |= BIT_ULL(4) | BIT_ULL(5);
-       else if (req->rx.strip_vtag)
+       if (req->rx.capture_vtag)
+               regval |= BIT_ULL(5);
+       if (req->rx.strip_vtag)
                regval |= BIT_ULL(4);
-       else
-               regval &= ~(BIT_ULL(4) | BIT_ULL(5));
-
-       regval &= ~NIX_VTAGSIZE_MASK;
-       regval |= req->vtag_size & NIX_VTAGSIZE_MASK;
 
        rvu_write64(rvu, blkaddr,
                    NIX_AF_LFX_RX_VTAG_TYPEX(nixlf, req->rx.vtag_type), regval);
        return 0;
 }
 
-int rvu_mbox_handler_NIX_VTAG_CFG(struct rvu *rvu,
+int rvu_mbox_handler_nix_vtag_cfg(struct rvu *rvu,
                                  struct nix_vtag_config *req,
                                  struct msg_rsp *rsp)
 {
@@ -1243,7 +1625,7 @@ static int nix_setup_mce(struct rvu *rvu, int mce, u8 op,
        struct nix_aq_enq_req aq_req;
        int err;
 
-       aq_req.hdr.pcifunc = pcifunc;
+       aq_req.hdr.pcifunc = 0;
        aq_req.ctype = NIX_AQ_CTYPE_MCE;
        aq_req.op = op;
        aq_req.qidx = mce;
@@ -1294,7 +1676,7 @@ static int nix_update_mce_list(struct nix_mce_list *mce_list,
                return 0;
 
        /* Add a new one to the list, at the tail */
-       mce = kzalloc(sizeof(*mce), GFP_ATOMIC);
+       mce = kzalloc(sizeof(*mce), GFP_KERNEL);
        if (!mce)
                return -ENOMEM;
        mce->idx = idx;
@@ -1317,6 +1699,10 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
        struct rvu_pfvf *pfvf;
        int blkaddr;
 
+       /* Broadcast pkt replication is not needed for AF's VFs, hence skip */
+       if (is_afvf(pcifunc))
+               return 0;
+
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
        if (blkaddr < 0)
                return 0;
@@ -1340,7 +1726,7 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
                return -EINVAL;
        }
 
-       spin_lock(&mcast->mce_lock);
+       mutex_lock(&mcast->mce_lock);
 
        err = nix_update_mce_list(mce_list, pcifunc, idx, add);
        if (err)
@@ -1370,7 +1756,7 @@ static int nix_update_bcast_mce_list(struct rvu *rvu, u16 pcifunc, bool add)
        }
 
 end:
-       spin_unlock(&mcast->mce_lock);
+       mutex_unlock(&mcast->mce_lock);
        return err;
 }
 
@@ -1455,7 +1841,7 @@ static int nix_setup_mcast(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
                    BIT_ULL(63) | (mcast->replay_pkind << 24) |
                    BIT_ULL(20) | MC_BUF_CNT);
 
-       spin_lock_init(&mcast->mce_lock);
+       mutex_init(&mcast->mce_lock);
 
        return nix_setup_bcast_tables(rvu, nix_hw);
 }
@@ -1499,14 +1885,66 @@ static int nix_setup_txschq(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
                 * PF/VF pcifunc mapping info.
                 */
                txsch->pfvf_map = devm_kcalloc(rvu->dev, txsch->schq.max,
-                                              sizeof(u16), GFP_KERNEL);
+                                              sizeof(u32), GFP_KERNEL);
                if (!txsch->pfvf_map)
                        return -ENOMEM;
+               memset(txsch->pfvf_map, U8_MAX, txsch->schq.max * sizeof(u32));
+       }
+       return 0;
+}
+
+int rvu_nix_reserve_mark_format(struct rvu *rvu, struct nix_hw *nix_hw,
+                               int blkaddr, u32 cfg)
+{
+       int fmt_idx;
+
+       for (fmt_idx = 0; fmt_idx < nix_hw->mark_format.in_use; fmt_idx++) {
+               if (nix_hw->mark_format.cfg[fmt_idx] == cfg)
+                       return fmt_idx;
        }
+       if (fmt_idx >= nix_hw->mark_format.total)
+               return -ERANGE;
+
+       rvu_write64(rvu, blkaddr, NIX_AF_MARK_FORMATX_CTL(fmt_idx), cfg);
+       nix_hw->mark_format.cfg[fmt_idx] = cfg;
+       nix_hw->mark_format.in_use++;
+       return fmt_idx;
+}
+
+static int nix_af_mark_format_setup(struct rvu *rvu, struct nix_hw *nix_hw,
+                                   int blkaddr)
+{
+       u64 cfgs[] = {
+               [NIX_MARK_CFG_IP_DSCP_RED]         = 0x10003,
+               [NIX_MARK_CFG_IP_DSCP_YELLOW]      = 0x11200,
+               [NIX_MARK_CFG_IP_DSCP_YELLOW_RED]  = 0x11203,
+               [NIX_MARK_CFG_IP_ECN_RED]          = 0x6000c,
+               [NIX_MARK_CFG_IP_ECN_YELLOW]       = 0x60c00,
+               [NIX_MARK_CFG_IP_ECN_YELLOW_RED]   = 0x60c0c,
+               [NIX_MARK_CFG_VLAN_DEI_RED]        = 0x30008,
+               [NIX_MARK_CFG_VLAN_DEI_YELLOW]     = 0x30800,
+               [NIX_MARK_CFG_VLAN_DEI_YELLOW_RED] = 0x30808,
+       };
+       int i, rc;
+       u64 total;
+
+       total = (rvu_read64(rvu, blkaddr, NIX_AF_PSE_CONST) & 0xFF00) >> 8;
+       nix_hw->mark_format.total = (u8)total;
+       nix_hw->mark_format.cfg = devm_kcalloc(rvu->dev, total, sizeof(u32),
+                                              GFP_KERNEL);
+       if (!nix_hw->mark_format.cfg)
+               return -ENOMEM;
+       for (i = 0; i < NIX_MARK_CFG_MAX; i++) {
+               rc = rvu_nix_reserve_mark_format(rvu, nix_hw, blkaddr, cfgs[i]);
+               if (rc < 0)
+                       dev_err(rvu->dev, "Err %d in setup mark format %d\n",
+                               i, rc);
+       }
+
        return 0;
 }
 
-int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_nix_stats_rst(struct rvu *rvu, struct msg_req *req,
                                   struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -1537,190 +1975,287 @@ int rvu_mbox_handler_NIX_STATS_RST(struct rvu *rvu, struct msg_req *req,
 }
 
 /* Returns the ALG index to be set into NPC_RX_ACTION */
-static int get_flowkey_alg_idx(u32 flow_cfg)
-{
-       u32 ip_cfg;
-
-       flow_cfg &= ~FLOW_KEY_TYPE_PORT;
-       ip_cfg = FLOW_KEY_TYPE_IPV4 | FLOW_KEY_TYPE_IPV6;
-       if (flow_cfg == ip_cfg)
-               return FLOW_KEY_ALG_IP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP))
-               return FLOW_KEY_ALG_TCP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_UDP))
-               return FLOW_KEY_ALG_UDP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_UDP))
-               return FLOW_KEY_ALG_TCP_UDP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_TCP_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_UDP_SCTP;
-       else if (flow_cfg == (ip_cfg | FLOW_KEY_TYPE_TCP |
-                             FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP))
-               return FLOW_KEY_ALG_TCP_UDP_SCTP;
-
-       return FLOW_KEY_ALG_PORT;
-}
-
-int rvu_mbox_handler_NIX_RSS_FLOWKEY_CFG(struct rvu *rvu,
-                                        struct nix_rss_flowkey_cfg *req,
-                                        struct msg_rsp *rsp)
+static int get_flowkey_alg_idx(struct nix_hw *nix_hw, u32 flow_cfg)
 {
-       struct rvu_hwinfo *hw = rvu->hw;
-       u16 pcifunc = req->hdr.pcifunc;
-       int alg_idx, nixlf, blkaddr;
-
-       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
-       if (blkaddr < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
-
-       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
-       if (nixlf < 0)
-               return NIX_AF_ERR_AF_LF_INVALID;
+       int i;
 
-       alg_idx = get_flowkey_alg_idx(req->flowkey_cfg);
+       /* Scan over exiting algo entries to find a match */
+       for (i = 0; i < nix_hw->flowkey.in_use; i++)
+               if (nix_hw->flowkey.flowkey[i] == flow_cfg)
+                       return i;
 
-       rvu_npc_update_flowkey_alg_idx(rvu, pcifunc, nixlf, req->group,
-                                      alg_idx, req->mcam_index);
-       return 0;
+       return -ERANGE;
 }
 
-static void set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
+static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
 {
-       struct nix_rx_flowkey_alg *field = NULL;
-       int idx, key_type;
+       int idx, nr_field, key_off, field_marker, keyoff_marker;
+       int max_key_off, max_bit_pos, group_member;
+       struct nix_rx_flowkey_alg *field;
+       struct nix_rx_flowkey_alg tmp;
+       u32 key_type, valid_key;
 
        if (!alg)
-               return;
+               return -EINVAL;
 
-       /* FIELD0: IPv4
-        * FIELD1: IPv6
-        * FIELD2: TCP/UDP/SCTP/ALL
-        * FIELD3: Unused
-        * FIELD4: Unused
-        *
-        * Each of the 32 possible flow key algorithm definitions should
+#define FIELDS_PER_ALG  5
+#define MAX_KEY_OFF    40
+       /* Clear all fields */
+       memset(alg, 0, sizeof(uint64_t) * FIELDS_PER_ALG);
+
+       /* Each of the 32 possible flow key algorithm definitions should
         * fall into above incremental config (except ALG0). Otherwise a
         * single NPC MCAM entry is not sufficient for supporting RSS.
         *
         * If a different definition or combination needed then NPC MCAM
         * has to be programmed to filter such pkts and it's action should
         * point to this definition to calculate flowtag or hash.
+        *
+        * The `for loop` goes over _all_ protocol field and the following
+        * variables depicts the state machine forward progress logic.
+        *
+        * keyoff_marker - Enabled when hash byte length needs to be accounted
+        * in field->key_offset update.
+        * field_marker - Enabled when a new field needs to be selected.
+        * group_member - Enabled when protocol is part of a group.
         */
-       for (idx = 0; idx < 32; idx++) {
-               key_type = flow_cfg & BIT_ULL(idx);
-               if (!key_type)
-                       continue;
+
+       keyoff_marker = 0; max_key_off = 0; group_member = 0;
+       nr_field = 0; key_off = 0; field_marker = 1;
+       field = &tmp; max_bit_pos = fls(flow_cfg);
+       for (idx = 0;
+            idx < max_bit_pos && nr_field < FIELDS_PER_ALG &&
+            key_off < MAX_KEY_OFF; idx++) {
+               key_type = BIT(idx);
+               valid_key = flow_cfg & key_type;
+               /* Found a field marker, reset the field values */
+               if (field_marker)
+                       memset(&tmp, 0, sizeof(tmp));
+
                switch (key_type) {
-               case FLOW_KEY_TYPE_PORT:
-                       field = &alg[0];
+               case NIX_FLOW_KEY_TYPE_PORT:
                        field->sel_chan = true;
                        /* This should be set to 1, when SEL_CHAN is set */
                        field->bytesm1 = 1;
+                       field_marker = true;
+                       keyoff_marker = true;
                        break;
-               case FLOW_KEY_TYPE_IPV4:
-                       field = &alg[0];
+               case NIX_FLOW_KEY_TYPE_IPV4:
                        field->lid = NPC_LID_LC;
                        field->ltype_match = NPC_LT_LC_IP;
                        field->hdr_offset = 12; /* SIP offset */
                        field->bytesm1 = 7; /* SIP + DIP, 8 bytes */
                        field->ltype_mask = 0xF; /* Match only IPv4 */
+                       field_marker = true;
+                       keyoff_marker = false;
                        break;
-               case FLOW_KEY_TYPE_IPV6:
-                       field = &alg[1];
+               case NIX_FLOW_KEY_TYPE_IPV6:
                        field->lid = NPC_LID_LC;
                        field->ltype_match = NPC_LT_LC_IP6;
                        field->hdr_offset = 8; /* SIP offset */
                        field->bytesm1 = 31; /* SIP + DIP, 32 bytes */
                        field->ltype_mask = 0xF; /* Match only IPv6 */
+                       field_marker = true;
+                       keyoff_marker = true;
                        break;
-               case FLOW_KEY_TYPE_TCP:
-               case FLOW_KEY_TYPE_UDP:
-               case FLOW_KEY_TYPE_SCTP:
-                       field = &alg[2];
+               case NIX_FLOW_KEY_TYPE_TCP:
+               case NIX_FLOW_KEY_TYPE_UDP:
+               case NIX_FLOW_KEY_TYPE_SCTP:
                        field->lid = NPC_LID_LD;
                        field->bytesm1 = 3; /* Sport + Dport, 4 bytes */
-                       if (key_type == FLOW_KEY_TYPE_TCP)
+                       if (key_type == NIX_FLOW_KEY_TYPE_TCP && valid_key) {
                                field->ltype_match |= NPC_LT_LD_TCP;
-                       else if (key_type == FLOW_KEY_TYPE_UDP)
+                               group_member = true;
+                       } else if (key_type == NIX_FLOW_KEY_TYPE_UDP &&
+                                  valid_key) {
                                field->ltype_match |= NPC_LT_LD_UDP;
-                       else if (key_type == FLOW_KEY_TYPE_SCTP)
+                               group_member = true;
+                       } else if (key_type == NIX_FLOW_KEY_TYPE_SCTP &&
+                                  valid_key) {
                                field->ltype_match |= NPC_LT_LD_SCTP;
-                       field->key_offset = 32; /* After IPv4/v6 SIP, DIP */
+                               group_member = true;
+                       }
                        field->ltype_mask = ~field->ltype_match;
+                       if (key_type == NIX_FLOW_KEY_TYPE_SCTP) {
+                               /* Handle the case where any of the group item
+                                * is enabled in the group but not the final one
+                                */
+                               if (group_member) {
+                                       valid_key = true;
+                                       group_member = false;
+                               }
+                               field_marker = true;
+                               keyoff_marker = true;
+                       } else {
+                               field_marker = false;
+                               keyoff_marker = false;
+                       }
                        break;
                }
-               if (field)
-                       field->ena = 1;
-               field = NULL;
+               field->ena = 1;
+
+               /* Found a valid flow key type */
+               if (valid_key) {
+                       field->key_offset = key_off;
+                       memcpy(&alg[nr_field], field, sizeof(*field));
+                       max_key_off = max(max_key_off, field->bytesm1 + 1);
+
+                       /* Found a field marker, get the next field */
+                       if (field_marker)
+                               nr_field++;
+               }
+
+               /* Found a keyoff marker, update the new key_off */
+               if (keyoff_marker) {
+                       key_off += max_key_off;
+                       max_key_off = 0;
+               }
        }
+       /* Processed all the flow key types */
+       if (idx == max_bit_pos && key_off <= MAX_KEY_OFF)
+               return 0;
+       else
+               return NIX_AF_ERR_RSS_NOSPC_FIELD;
 }
 
-static void nix_rx_flowkey_alg_cfg(struct rvu *rvu, int blkaddr)
+static int reserve_flowkey_alg_idx(struct rvu *rvu, int blkaddr, u32 flow_cfg)
 {
-#define FIELDS_PER_ALG 5
-       u64 field[FLOW_KEY_ALG_MAX][FIELDS_PER_ALG];
-       u32 flowkey_cfg, minkey_cfg;
-       int alg, fid;
+       u64 field[FIELDS_PER_ALG];
+       struct nix_hw *hw;
+       int fid, rc;
 
-       memset(&field, 0, sizeof(u64) * FLOW_KEY_ALG_MAX * FIELDS_PER_ALG);
+       hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!hw)
+               return -EINVAL;
 
-       /* Only incoming channel number */
-       flowkey_cfg = FLOW_KEY_TYPE_PORT;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_PORT], flowkey_cfg);
+       /* No room to add new flow hash algoritham */
+       if (hw->flowkey.in_use >= NIX_FLOW_KEY_ALG_MAX)
+               return NIX_AF_ERR_RSS_NOSPC_ALGO;
 
-       /* For a incoming pkt if none of the fields match then flowkey
-        * will be zero, hence tag generated will also be zero.
-        * RSS entry at rsse_index = NIX_AF_LF()_RSS_GRP()[OFFSET] will
-        * be used to queue the packet.
-        */
+       /* Generate algo fields for the given flow_cfg */
+       rc = set_flowkey_fields((struct nix_rx_flowkey_alg *)field, flow_cfg);
+       if (rc)
+               return rc;
 
-       /* IPv4/IPv6 SIP/DIPs */
-       flowkey_cfg = FLOW_KEY_TYPE_IPV4 | FLOW_KEY_TYPE_IPV6;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_IP], flowkey_cfg);
+       /* Update ALGX_FIELDX register with generated fields */
+       for (fid = 0; fid < FIELDS_PER_ALG; fid++)
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(hw->flowkey.in_use,
+                                                          fid), field[fid]);
 
-       /* TCPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
+       /* Store the flow_cfg for futher lookup */
+       rc = hw->flowkey.in_use;
+       hw->flowkey.flowkey[rc] = flow_cfg;
+       hw->flowkey.in_use++;
+
+       return rc;
+}
+
+int rvu_mbox_handler_nix_rss_flowkey_cfg(struct rvu *rvu,
+                                        struct nix_rss_flowkey_cfg *req,
+                                        struct nix_rss_flowkey_cfg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       int alg_idx, nixlf, blkaddr;
+       struct nix_hw *nix_hw;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       alg_idx = get_flowkey_alg_idx(nix_hw, req->flowkey_cfg);
+       /* Failed to get algo index from the exiting list, reserve new  */
+       if (alg_idx < 0) {
+               alg_idx = reserve_flowkey_alg_idx(rvu, blkaddr,
+                                                 req->flowkey_cfg);
+               if (alg_idx < 0)
+                       return alg_idx;
+       }
+       rsp->alg_idx = alg_idx;
+       rvu_npc_update_flowkey_alg_idx(rvu, pcifunc, nixlf, req->group,
+                                      alg_idx, req->mcam_index);
+       return 0;
+}
+
+static int nix_rx_flowkey_alg_cfg(struct rvu *rvu, int blkaddr)
+{
+       u32 flowkey_cfg, minkey_cfg;
+       int alg, fid, rc;
+
+       /* Disable all flow key algx fieldx */
+       for (alg = 0; alg < NIX_FLOW_KEY_ALG_MAX; alg++) {
+               for (fid = 0; fid < FIELDS_PER_ALG; fid++)
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(alg, fid),
+                                   0);
+       }
+
+       /* IPv4/IPv6 SIP/DIPs */
+       flowkey_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
+
+       /* TCPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
        minkey_cfg = flowkey_cfg;
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* UDPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_UDP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_UDP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_UDP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* SCTPv4/v6 4-tuple, SIP, DIP, Sport, Dport */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/UDP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_UDP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_UDP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                       NIX_FLOW_KEY_TYPE_UDP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                       NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* UDP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_UDP_SCTP], flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_UDP |
+                       NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
        /* TCP/UDP/SCTP v4/v6 4-tuple, rest IP pkts 2-tuple */
-       flowkey_cfg = minkey_cfg | FLOW_KEY_TYPE_TCP |
-                     FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP;
-       set_flowkey_fields((void *)&field[FLOW_KEY_ALG_TCP_UDP_SCTP],
-                          flowkey_cfg);
+       flowkey_cfg = minkey_cfg | NIX_FLOW_KEY_TYPE_TCP |
+                     NIX_FLOW_KEY_TYPE_UDP | NIX_FLOW_KEY_TYPE_SCTP;
+       rc = reserve_flowkey_alg_idx(rvu, blkaddr, flowkey_cfg);
+       if (rc < 0)
+               return rc;
 
-       for (alg = 0; alg < FLOW_KEY_ALG_MAX; alg++) {
-               for (fid = 0; fid < FIELDS_PER_ALG; fid++)
-                       rvu_write64(rvu, blkaddr,
-                                   NIX_AF_RX_FLOW_KEY_ALGX_FIELDX(alg, fid),
-                                   field[alg][fid]);
-       }
+       return 0;
 }
 
-int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
+int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu,
                                      struct nix_set_mac_addr *req,
                                      struct msg_rsp *rsp)
 {
@@ -1742,10 +2277,13 @@ int rvu_mbox_handler_NIX_SET_MAC_ADDR(struct rvu *rvu,
 
        rvu_npc_install_ucast_entry(rvu, pcifunc, nixlf,
                                    pfvf->rx_chan_base, req->mac_addr);
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+
        return 0;
 }
 
-int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
+int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
                                     struct msg_rsp *rsp)
 {
        bool allmulti = false, disable_promisc = false;
@@ -1775,9 +2313,303 @@ int rvu_mbox_handler_NIX_SET_RX_MODE(struct rvu *rvu, struct nix_rx_mode *req,
        else
                rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf,
                                              pfvf->rx_chan_base, allmulti);
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+
        return 0;
 }
 
+static void nix_find_link_frs(struct rvu *rvu,
+                             struct nix_frs_cfg *req, u16 pcifunc)
+{
+       int pf = rvu_get_pf(pcifunc);
+       struct rvu_pfvf *pfvf;
+       int maxlen, minlen;
+       int numvfs, hwvf;
+       int vf;
+
+       /* Update with requester's min/max lengths */
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       pfvf->maxlen = req->maxlen;
+       if (req->update_minlen)
+               pfvf->minlen = req->minlen;
+
+       maxlen = req->maxlen;
+       minlen = req->update_minlen ? req->minlen : 0;
+
+       /* Get this PF's numVFs and starting hwvf */
+       rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf);
+
+       /* For each VF, compare requested max/minlen */
+       for (vf = 0; vf < numvfs; vf++) {
+               pfvf =  &rvu->hwvf[hwvf + vf];
+               if (pfvf->maxlen > maxlen)
+                       maxlen = pfvf->maxlen;
+               if (req->update_minlen &&
+                   pfvf->minlen && pfvf->minlen < minlen)
+                       minlen = pfvf->minlen;
+       }
+
+       /* Compare requested max/minlen with PF's max/minlen */
+       pfvf = &rvu->pf[pf];
+       if (pfvf->maxlen > maxlen)
+               maxlen = pfvf->maxlen;
+       if (req->update_minlen &&
+           pfvf->minlen && pfvf->minlen < minlen)
+               minlen = pfvf->minlen;
+
+       /* Update the request with max/min PF's and it's VF's max/min */
+       req->maxlen = maxlen;
+       if (req->update_minlen)
+               req->minlen = minlen;
+}
+
+int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
+                                   struct msg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       int pf = rvu_get_pf(pcifunc);
+       int blkaddr, schq, link = -1;
+       struct nix_txsch *txsch;
+       u64 cfg, lmac_fifo_len;
+       struct nix_hw *nix_hw;
+       u8 cgx = 0, lmac = 0;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       if (!req->sdp_link && req->maxlen > NIC_HW_MAX_FRS)
+               return NIX_AF_ERR_FRS_INVALID;
+
+       if (req->update_minlen && req->minlen < NIC_HW_MIN_FRS)
+               return NIX_AF_ERR_FRS_INVALID;
+
+       /* Check if requester wants to update SMQ's */
+       if (!req->update_smq)
+               goto rx_frscfg;
+
+       /* Update min/maxlen in each of the SMQ attached to this PF/VF */
+       txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
+       mutex_lock(&rvu->rsrc_lock);
+       for (schq = 0; schq < txsch->schq.max; schq++) {
+               if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
+                       continue;
+               cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
+               cfg = (cfg & ~(0xFFFFULL << 8)) | ((u64)req->maxlen << 8);
+               if (req->update_minlen)
+                       cfg = (cfg & ~0x7FULL) | ((u64)req->minlen & 0x7F);
+               rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg);
+       }
+       mutex_unlock(&rvu->rsrc_lock);
+
+rx_frscfg:
+       /* Check if config is for SDP link */
+       if (req->sdp_link) {
+               if (!hw->sdp_links)
+                       return NIX_AF_ERR_RX_LINK_INVALID;
+               link = hw->cgx_links + hw->lbk_links;
+               goto linkcfg;
+       }
+
+       /* Check if the request is from CGX mapped RVU PF */
+       if (is_pf_cgxmapped(rvu, pf)) {
+               /* Get CGX and LMAC to which this PF is mapped and find link */
+               rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx, &lmac);
+               link = (cgx * hw->lmac_per_cgx) + lmac;
+       } else if (pf == 0) {
+               /* For VFs of PF0 ingress is LBK port, so config LBK link */
+               link = hw->cgx_links;
+       }
+
+       if (link < 0)
+               return NIX_AF_ERR_RX_LINK_INVALID;
+
+       nix_find_link_frs(rvu, req, pcifunc);
+
+linkcfg:
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link));
+       cfg = (cfg & ~(0xFFFFULL << 16)) | ((u64)req->maxlen << 16);
+       if (req->update_minlen)
+               cfg = (cfg & ~0xFFFFULL) | req->minlen;
+       rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link), cfg);
+
+       if (req->sdp_link || pf == 0)
+               return 0;
+
+       /* Update transmit credits for CGX links */
+       lmac_fifo_len =
+               CGX_FIFO_LEN / cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link));
+       cfg &= ~(0xFFFFFULL << 12);
+       cfg |=  ((lmac_fifo_len - req->maxlen) / 16) << 12;
+       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
+       rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_EXPR_CREDIT(link), cfg);
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_rxvlan_alloc(struct rvu *rvu, struct msg_req *req,
+                                     struct msg_rsp *rsp)
+{
+       struct npc_mcam_alloc_entry_req alloc_req = { };
+       struct npc_mcam_alloc_entry_rsp alloc_rsp = { };
+       struct npc_mcam_free_entry_req free_req = { };
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, nixlf, err;
+       struct rvu_pfvf *pfvf;
+
+       /* LBK VFs do not have separate MCAM UCAST entry hence
+        * skip allocating rxvlan for them
+        */
+       if (is_afvf(pcifunc))
+               return 0;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       if (pfvf->rxvlan)
+               return 0;
+
+       /* alloc new mcam entry */
+       alloc_req.hdr.pcifunc = pcifunc;
+       alloc_req.count = 1;
+
+       err = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &alloc_req,
+                                                   &alloc_rsp);
+       if (err)
+               return err;
+
+       /* update entry to enable rxvlan offload */
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (blkaddr < 0) {
+               err = NIX_AF_ERR_AF_LF_INVALID;
+               goto free_entry;
+       }
+
+       nixlf = rvu_get_lf(rvu, &rvu->hw->block[blkaddr], pcifunc, 0);
+       if (nixlf < 0) {
+               err = NIX_AF_ERR_AF_LF_INVALID;
+               goto free_entry;
+       }
+
+       pfvf->rxvlan_index = alloc_rsp.entry_list[0];
+       /* all it means is that rxvlan_index is valid */
+       pfvf->rxvlan = true;
+
+       err = rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+       if (err)
+               goto free_entry;
+
+       return 0;
+free_entry:
+       free_req.hdr.pcifunc = pcifunc;
+       free_req.entry = alloc_rsp.entry_list[0];
+       rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, rsp);
+       pfvf->rxvlan = false;
+       return err;
+}
+
+int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
+                                   struct msg_rsp *rsp)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       u16 pcifunc = req->hdr.pcifunc;
+       struct rvu_block *block;
+       struct rvu_pfvf *pfvf;
+       int nixlf, blkaddr;
+       u64 cfg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       block = &hw->block[blkaddr];
+       nixlf = rvu_get_lf(rvu, block, pcifunc, 0);
+       if (nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       cfg = rvu_read64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf));
+       /* Set the interface configuration */
+       if (req->len_verify & BIT(0))
+               cfg |= BIT_ULL(41);
+       else
+               cfg &= ~BIT_ULL(41);
+
+       if (req->len_verify & BIT(1))
+               cfg |= BIT_ULL(40);
+       else
+               cfg &= ~BIT_ULL(40);
+
+       if (req->csum_verify & BIT(0))
+               cfg |= BIT_ULL(37);
+       else
+               cfg &= ~BIT_ULL(37);
+
+       rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), cfg);
+
+       return 0;
+}
+
+static void nix_link_config(struct rvu *rvu, int blkaddr)
+{
+       struct rvu_hwinfo *hw = rvu->hw;
+       int cgx, lmac_cnt, slink, link;
+       u64 tx_credits;
+
+       /* Set default min/max packet lengths allowed on NIX Rx links.
+        *
+        * With HW reset minlen value of 60byte, HW will treat ARP pkts
+        * as undersize and report them to SW as error pkts, hence
+        * setting it to 40 bytes.
+        */
+       for (link = 0; link < (hw->cgx_links + hw->lbk_links); link++) {
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
+                           NIC_HW_MAX_FRS << 16 | NIC_HW_MIN_FRS);
+       }
+
+       if (hw->sdp_links) {
+               link = hw->cgx_links + hw->lbk_links;
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link),
+                           SDP_HW_MAX_FRS << 16 | NIC_HW_MIN_FRS);
+       }
+
+       /* Set credits for Tx links assuming max packet length allowed.
+        * This will be reconfigured based on MTU set for PF/VF.
+        */
+       for (cgx = 0; cgx < hw->cgx; cgx++) {
+               lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
+               tx_credits = ((CGX_FIFO_LEN / lmac_cnt) - NIC_HW_MAX_FRS) / 16;
+               /* Enable credits and set credit pkt count to max allowed */
+               tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
+               slink = cgx * hw->lmac_per_cgx;
+               for (link = slink; link < (slink + lmac_cnt); link++) {
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_TX_LINKX_NORM_CREDIT(link),
+                                   tx_credits);
+                       rvu_write64(rvu, blkaddr,
+                                   NIX_AF_TX_LINKX_EXPR_CREDIT(link),
+                                   tx_credits);
+               }
+       }
+
+       /* Set Tx credits for LBK link */
+       slink = hw->cgx_links;
+       for (link = slink; link < (slink + hw->lbk_links); link++) {
+               tx_credits = 1000; /* 10 * max LBK datarate = 10 * 100Gbps */
+               /* Enable credits and set credit pkt count to max allowed */
+               tx_credits =  (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1);
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_TX_LINKX_NORM_CREDIT(link), tx_credits);
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_TX_LINKX_EXPR_CREDIT(link), tx_credits);
+       }
+}
+
 static int nix_calibrate_x2p(struct rvu *rvu, int blkaddr)
 {
        int idx, err;
@@ -1796,8 +2628,10 @@ static int nix_calibrate_x2p(struct rvu *rvu, int blkaddr)
 
        status = rvu_read64(rvu, blkaddr, NIX_AF_STATUS);
        /* Check if CGX devices are ready */
-       for (idx = 0; idx < cgx_get_cgx_cnt(); idx++) {
-               if (status & (BIT_ULL(16 + idx)))
+       for (idx = 0; idx < rvu->cgx_cnt_max; idx++) {
+               /* Skip when cgx port is not available */
+               if (!rvu_cgx_pdata(idx, rvu) ||
+                   (status & (BIT_ULL(16 + idx))))
                        continue;
                dev_err(rvu->dev,
                        "CGX%d didn't respond to NIX X2P calibration\n", idx);
@@ -1830,10 +2664,10 @@ static int nix_aq_init(struct rvu *rvu, struct rvu_block *block)
        /* Set admin queue endianness */
        cfg = rvu_read64(rvu, block->addr, NIX_AF_CFG);
 #ifdef __BIG_ENDIAN
-       cfg |= BIT_ULL(1);
+       cfg |= BIT_ULL(8);
        rvu_write64(rvu, block->addr, NIX_AF_CFG, cfg);
 #else
-       cfg &= ~BIT_ULL(1);
+       cfg &= ~BIT_ULL(8);
        rvu_write64(rvu, block->addr, NIX_AF_CFG, cfg);
 #endif
 
@@ -1870,6 +2704,14 @@ int rvu_nix_init(struct rvu *rvu)
                return 0;
        block = &hw->block[blkaddr];
 
+       /* As per a HW errata in 9xxx A0 silicon, NIX may corrupt
+        * internal state when conditional clocks are turned off.
+        * Hence enable them.
+        */
+       if (is_rvu_9xxx_A0(rvu))
+               rvu_write64(rvu, blkaddr, NIX_AF_CFG,
+                           rvu_read64(rvu, blkaddr, NIX_AF_CFG) | 0x5EULL);
+
        /* Calibrate X2P bus to check if CGX/LBK links are fine */
        err = nix_calibrate_x2p(rvu, blkaddr);
        if (err)
@@ -1891,9 +2733,6 @@ int rvu_nix_init(struct rvu *rvu)
        /* Restore CINT timer delay to HW reset values */
        rvu_write64(rvu, blkaddr, NIX_AF_CINT_DELAY, 0x0ULL);
 
-       /* Configure segmentation offload formats */
-       nix_setup_lso(rvu, blkaddr);
-
        if (blkaddr == BLKADDR_NIX0) {
                hw->nix0 = devm_kzalloc(rvu->dev,
                                        sizeof(struct nix_hw), GFP_KERNEL);
@@ -1904,24 +2743,51 @@ int rvu_nix_init(struct rvu *rvu)
                if (err)
                        return err;
 
+               err = nix_af_mark_format_setup(rvu, hw->nix0, blkaddr);
+               if (err)
+                       return err;
+
                err = nix_setup_mcast(rvu, hw->nix0, blkaddr);
                if (err)
                        return err;
 
-               /* Config Outer L2, IP, TCP and UDP's NPC layer info.
+               /* Configure segmentation offload formats */
+               nix_setup_lso(rvu, hw->nix0, blkaddr);
+
+               /* Config Outer/Inner L2, IP, TCP, UDP and SCTP NPC layer info.
                 * This helps HW protocol checker to identify headers
                 * and validate length and checksums.
                 */
                rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OL2,
                            (NPC_LID_LA << 8) | (NPC_LT_LA_ETHER << 4) | 0x0F);
-               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OUDP,
-                           (NPC_LID_LD << 8) | (NPC_LT_LD_UDP << 4) | 0x0F);
-               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OTCP,
-                           (NPC_LID_LD << 8) | (NPC_LT_LD_TCP << 4) | 0x0F);
                rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP4,
                            (NPC_LID_LC << 8) | (NPC_LT_LC_IP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP4,
+                           (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OIP6,
+                           (NPC_LID_LC << 8) | (NPC_LT_LC_IP6 << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IIP6,
+                           (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP6 << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OTCP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_TCP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ITCP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_TCP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OUDP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_UDP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_IUDP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_UDP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_OSCTP,
+                           (NPC_LID_LD << 8) | (NPC_LT_LD_SCTP << 4) | 0x0F);
+               rvu_write64(rvu, blkaddr, NIX_AF_RX_DEF_ISCTP,
+                           (NPC_LID_LG << 8) | (NPC_LT_LG_TU_SCTP << 4) |
+                           0x0F);
+
+               err = nix_rx_flowkey_alg_cfg(rvu, blkaddr);
+               if (err)
+                       return err;
 
-               nix_rx_flowkey_alg_cfg(rvu, blkaddr);
+               /* Initialize CGX/LBK/SDP link credits, min/max pkt lengths */
+               nix_link_config(rvu, blkaddr);
        }
        return 0;
 }
@@ -1955,5 +2821,139 @@ void rvu_nix_freemem(struct rvu *rvu)
                mcast = &nix_hw->mcast;
                qmem_free(rvu->dev, mcast->mce_ctx);
                qmem_free(rvu->dev, mcast->mcast_buf);
+               mutex_destroy(&mcast->mce_lock);
+       }
+}
+
+static int nix_get_nixlf(struct rvu *rvu, u16 pcifunc, int *nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct rvu_hwinfo *hw = rvu->hw;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       *nixlf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
+       if (*nixlf < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       return 0;
+}
+
+int rvu_mbox_handler_nix_lf_start_rx(struct rvu *rvu, struct msg_req *req,
+                                    struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       int nixlf, err;
+
+       err = nix_get_nixlf(rvu, pcifunc, &nixlf);
+       if (err)
+               return err;
+
+       rvu_npc_enable_default_entries(rvu, pcifunc, nixlf);
+       return 0;
+}
+
+int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
+                                   struct msg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       int nixlf, err;
+
+       err = nix_get_nixlf(rvu, pcifunc, &nixlf);
+       if (err)
+               return err;
+
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
+       return 0;
+}
+
+void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct hwctx_disable_req ctx_req;
+       int err;
+
+       ctx_req.hdr.pcifunc = pcifunc;
+
+       /* Cleanup NPC MCAM entries, free Tx scheduler queues being used */
+       nix_interface_deinit(rvu, pcifunc, nixlf);
+       nix_rx_sync(rvu, blkaddr);
+       nix_txschq_free(rvu, pcifunc);
+
+       if (pfvf->sq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_SQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "SQ ctx disable failed\n");
+       }
+
+       if (pfvf->rq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_RQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "RQ ctx disable failed\n");
        }
+
+       if (pfvf->cq_ctx) {
+               ctx_req.ctype = NIX_AQ_CTYPE_CQ;
+               err = nix_lf_hwctx_disable(rvu, &ctx_req);
+               if (err)
+                       dev_err(rvu->dev, "CQ ctx disable failed\n");
+       }
+
+       nix_ctx_free(rvu, pfvf);
+}
+
+int rvu_mbox_handler_nix_lso_format_cfg(struct rvu *rvu,
+                                       struct nix_lso_format_cfg *req,
+                                       struct nix_lso_format_cfg_rsp *rsp)
+{
+       u16 pcifunc = req->hdr.pcifunc;
+       struct nix_hw *nix_hw;
+       struct rvu_pfvf *pfvf;
+       int blkaddr, idx, f;
+       u64 reg;
+
+       pfvf = rvu_get_pfvf(rvu, pcifunc);
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
+       if (!pfvf->nixlf || blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       nix_hw = get_nix_hw(rvu->hw, blkaddr);
+       if (!nix_hw)
+               return -EINVAL;
+
+       /* Find existing matching LSO format, if any */
+       for (idx = 0; idx < nix_hw->lso.in_use; idx++) {
+               for (f = 0; f < NIX_LSO_FIELD_MAX; f++) {
+                       reg = rvu_read64(rvu, blkaddr,
+                                        NIX_AF_LSO_FORMATX_FIELDX(idx, f));
+                       if (req->fields[f] != (reg & req->field_mask))
+                               break;
+               }
+
+               if (f == NIX_LSO_FIELD_MAX)
+                       break;
+       }
+
+       if (idx < nix_hw->lso.in_use) {
+               /* Match found */
+               rsp->lso_format_idx = idx;
+               return 0;
+       }
+
+       if (nix_hw->lso.in_use == nix_hw->lso.total)
+               return NIX_AF_ERR_LSO_CFG_FAIL;
+
+       rsp->lso_format_idx = nix_hw->lso.in_use++;
+
+       for (f = 0; f < NIX_LSO_FIELD_MAX; f++)
+               rvu_write64(rvu, blkaddr,
+                           NIX_AF_LSO_FORMATX_FIELDX(rsp->lso_format_idx, f),
+                           req->fields[f]);
+
+       return 0;
 }
index 7531fdc..c0e165d 100644 (file)
@@ -241,14 +241,14 @@ static int npa_lf_hwctx_disable(struct rvu *rvu, struct hwctx_disable_req *req)
        return err;
 }
 
-int rvu_mbox_handler_NPA_AQ_ENQ(struct rvu *rvu,
+int rvu_mbox_handler_npa_aq_enq(struct rvu *rvu,
                                struct npa_aq_enq_req *req,
                                struct npa_aq_enq_rsp *rsp)
 {
        return rvu_npa_aq_enq_inst(rvu, req, rsp);
 }
 
-int rvu_mbox_handler_NPA_HWCTX_DISABLE(struct rvu *rvu,
+int rvu_mbox_handler_npa_hwctx_disable(struct rvu *rvu,
                                       struct hwctx_disable_req *req,
                                       struct msg_rsp *rsp)
 {
@@ -273,7 +273,7 @@ static void npa_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
        pfvf->npa_qints_ctx = NULL;
 }
 
-int rvu_mbox_handler_NPA_LF_ALLOC(struct rvu *rvu,
+int rvu_mbox_handler_npa_lf_alloc(struct rvu *rvu,
                                  struct npa_lf_alloc_req *req,
                                  struct npa_lf_alloc_rsp *rsp)
 {
@@ -372,7 +372,7 @@ exit:
        return rc;
 }
 
-int rvu_mbox_handler_NPA_LF_FREE(struct rvu *rvu, struct msg_req *req,
+int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req,
                                 struct msg_rsp *rsp)
 {
        struct rvu_hwinfo *hw = rvu->hw;
@@ -470,3 +470,20 @@ void rvu_npa_freemem(struct rvu *rvu)
        block = &hw->block[blkaddr];
        rvu_aq_free(rvu, block->aq);
 }
+
+void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct hwctx_disable_req ctx_req;
+
+       /* Disable all pools */
+       ctx_req.hdr.pcifunc = pcifunc;
+       ctx_req.ctype = NPA_AQ_CTYPE_POOL;
+       npa_lf_hwctx_disable(rvu, &ctx_req);
+
+       /* Disable all auras */
+       ctx_req.ctype = NPA_AQ_CTYPE_AURA;
+       npa_lf_hwctx_disable(rvu, &ctx_req);
+
+       npa_ctx_free(rvu, pfvf);
+}
index 23ff47f..15f7027 100644 (file)
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 
@@ -15,6 +16,7 @@
 #include "rvu_reg.h"
 #include "rvu.h"
 #include "npc.h"
+#include "cgx.h"
 #include "npc_profile.h"
 
 #define RSVD_MCAM_ENTRIES_PER_PF       2 /* Bcast & Promisc */
 
 #define NPC_PARSE_RESULT_DMAC_OFFSET   8
 
-struct mcam_entry {
-#define NPC_MAX_KWS_IN_KEY     7 /* Number of keywords in max keywidth */
-       u64     kw[NPC_MAX_KWS_IN_KEY];
-       u64     kw_mask[NPC_MAX_KWS_IN_KEY];
-       u64     action;
-       u64     vtag_action;
-};
+static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
+                                     int blkaddr, u16 pcifunc);
+static void npc_mcam_free_all_counters(struct rvu *rvu, struct npc_mcam *mcam,
+                                      u16 pcifunc);
 
 void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf)
 {
@@ -256,6 +255,46 @@ static void npc_config_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
                npc_enable_mcam_entry(rvu, mcam, blkaddr, actindex, false);
 }
 
+static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
+                               int blkaddr, u16 src, u16 dest)
+{
+       int dbank = npc_get_bank(mcam, dest);
+       int sbank = npc_get_bank(mcam, src);
+       u64 cfg, sreg, dreg;
+       int bank, i;
+
+       src &= (mcam->banksize - 1);
+       dest &= (mcam->banksize - 1);
+
+       /* Copy INTF's, W0's, W1's CAM0 and CAM1 configuration */
+       for (bank = 0; bank < mcam->banks_per_entry; bank++) {
+               sreg = NPC_AF_MCAMEX_BANKX_CAMX_INTF(src, sbank + bank, 0);
+               dreg = NPC_AF_MCAMEX_BANKX_CAMX_INTF(dest, dbank + bank, 0);
+               for (i = 0; i < 6; i++) {
+                       cfg = rvu_read64(rvu, blkaddr, sreg + (i * 8));
+                       rvu_write64(rvu, blkaddr, dreg + (i * 8), cfg);
+               }
+       }
+
+       /* Copy action */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_ACTION(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_ACTION(dest, dbank), cfg);
+
+       /* Copy TAG action */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_TAG_ACT(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_TAG_ACT(dest, dbank), cfg);
+
+       /* Enable or disable */
+       cfg = rvu_read64(rvu, blkaddr,
+                        NPC_AF_MCAMEX_BANKX_CFG(src, sbank));
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg);
+}
+
 static u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
                               int blkaddr, int index)
 {
@@ -269,12 +308,17 @@ static u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
 void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
                                 int nixlf, u64 chan, u8 *mac_addr)
 {
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct mcam_entry entry = { {0} };
        struct nix_rx_action action;
        int blkaddr, index, kwi;
        u64 mac = 0;
 
+       /* AF's VFs work in promiscuous mode */
+       if (is_afvf(pcifunc))
+               return;
+
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return;
@@ -308,22 +352,33 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
        entry.action = *(u64 *)&action;
        npc_config_mcam_entry(rvu, mcam, blkaddr, index,
                              NIX_INTF_RX, &entry, true);
+
+       /* add VLAN matching, setup action and save entry back for later */
+       entry.kw[0] |= (NPC_LT_LB_STAG | NPC_LT_LB_CTAG) << 20;
+       entry.kw_mask[0] |= (NPC_LT_LB_STAG & NPC_LT_LB_CTAG) << 20;
+
+       entry.vtag_action = VTAG0_VALID_BIT |
+                           FIELD_PREP(VTAG0_TYPE_MASK, 0) |
+                           FIELD_PREP(VTAG0_LID_MASK, NPC_LID_LA) |
+                           FIELD_PREP(VTAG0_RELPTR_MASK, 12);
+
+       memcpy(&pfvf->entry, &entry, sizeof(entry));
 }
 
 void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                                   int nixlf, u64 chan, bool allmulti)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, ucast_idx, index, kwi;
        struct mcam_entry entry = { {0} };
-       struct nix_rx_action action;
-       int blkaddr, index, kwi;
+       struct nix_rx_action action = { };
 
-       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
-       if (blkaddr < 0)
+       /* Only PF or AF VF can add a promiscuous entry */
+       if ((pcifunc & RVU_PFVF_FUNC_MASK) && !is_afvf(pcifunc))
                return;
 
-       /* Only PF or AF VF can add a promiscuous entry */
-       if (pcifunc & RVU_PFVF_FUNC_MASK)
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
                return;
 
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
@@ -338,16 +393,29 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
                entry.kw_mask[kwi] = BIT_ULL(40);
        }
 
-       *(u64 *)&action = 0x00;
-       action.op = NIX_RX_ACTIONOP_UCAST;
-       action.pf_func = pcifunc;
+       ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                            nixlf, NIXLF_UCAST_ENTRY);
+
+       /* If the corresponding PF's ucast action is RSS,
+        * use the same action for promisc also
+        */
+       if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx))
+               *(u64 *)&action = npc_get_mcam_action(rvu, mcam,
+                                                       blkaddr, ucast_idx);
+
+       if (action.op != NIX_RX_ACTIONOP_RSS) {
+               *(u64 *)&action = 0x00;
+               action.op = NIX_RX_ACTIONOP_UCAST;
+               action.pf_func = pcifunc;
+       }
 
        entry.action = *(u64 *)&action;
        npc_config_mcam_entry(rvu, mcam, blkaddr, index,
                              NIX_INTF_RX, &entry, true);
 }
 
-void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+static void npc_enadis_promisc_entry(struct rvu *rvu, u16 pcifunc,
+                                    int nixlf, bool enable)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
        int blkaddr, index;
@@ -362,7 +430,17 @@ void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
 
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_PROMISC_ENTRY);
-       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
+}
+
+void rvu_npc_disable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_promisc_entry(rvu, pcifunc, nixlf, false);
+}
+
+void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_promisc_entry(rvu, pcifunc, nixlf, true);
 }
 
 void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
@@ -390,9 +468,28 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_BCAST_ENTRY);
 
-       /* Check for L2B bit and LMAC channel */
-       entry.kw[0] = BIT_ULL(25) | chan;
-       entry.kw_mask[0] = BIT_ULL(25) | 0xFFFULL;
+       /* Check for L2B bit and LMAC channel
+        * NOTE: Since MKEX default profile(a reduced version intended to
+        * accommodate more capability but igoring few bits) a stap-gap
+        * approach.
+        * Since we care for L2B which by HRM NPC_PARSE_KEX_S at BIT_POS[25], So
+        * moved to BIT_POS[13], ignoring ERRCODE, ERRLEV as we'll loose out
+        * on capability features needed for CoS (/from ODP PoV) e.g: VLAN,
+        * DSCP.
+        *
+        * Reduced layout of MKEX default profile -
+        * Includes following are (i.e.CHAN, L2/3{B/M}, LA, LB, LC, LD):
+        *
+        * BIT_POS[31:28] : LD
+        * BIT_POS[27:24] : LC
+        * BIT_POS[23:20] : LB
+        * BIT_POS[19:16] : LA
+        * BIT_POS[15:12] : L3B, L3M, L2B, L2M
+        * BIT_POS[11:00] : CHAN
+        *
+        */
+       entry.kw[0] = BIT_ULL(13) | chan;
+       entry.kw_mask[0] = BIT_ULL(13) | 0xFFFULL;
 
        *(u64 *)&action = 0x00;
 #ifdef MCAST_MCE
@@ -454,51 +551,110 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
 
        rvu_write64(rvu, blkaddr,
                    NPC_AF_MCAMEX_BANKX_ACTION(index, bank), *(u64 *)&action);
+
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                        nixlf, NIXLF_PROMISC_ENTRY);
+
+       /* If PF's promiscuous entry is enabled,
+        * Set RSS action for that entry as well
+        */
+       if (is_mcam_entry_enabled(rvu, mcam, blkaddr, index)) {
+               bank = npc_get_bank(mcam, index);
+               index &= (mcam->banksize - 1);
+
+               rvu_write64(rvu, blkaddr,
+                           NPC_AF_MCAMEX_BANKX_ACTION(index, bank),
+                           *(u64 *)&action);
+       }
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
 }
 
-void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
+                                      int nixlf, bool enable)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
        struct nix_rx_action action;
-       int blkaddr, index, bank;
+       int index, bank, blkaddr;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0)
                return;
 
-       /* Disable ucast MCAM match entry of this PF/VF */
+       /* Ucast MCAM match entry of this PF/VF */
        index = npc_get_nixlf_mcam_index(mcam, pcifunc,
                                         nixlf, NIXLF_UCAST_ENTRY);
-       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
 
-       /* For PF, disable promisc and bcast MCAM match entries */
-       if (!(pcifunc & RVU_PFVF_FUNC_MASK)) {
-               index = npc_get_nixlf_mcam_index(mcam, pcifunc,
-                                                nixlf, NIXLF_BCAST_ENTRY);
-               /* For bcast, disable only if it's action is not
-                * packet replication, incase if action is replication
-                * then this PF's nixlf is removed from bcast replication
-                * list.
-                */
-               bank = npc_get_bank(mcam, index);
-               index &= (mcam->banksize - 1);
-               *(u64 *)&action = rvu_read64(rvu, blkaddr,
-                                    NPC_AF_MCAMEX_BANKX_ACTION(index, bank));
-               if (action.op != NIX_RX_ACTIONOP_MCAST)
-                       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+       /* For PF, ena/dis promisc and bcast MCAM match entries */
+       if (pcifunc & RVU_PFVF_FUNC_MASK)
+               return;
 
+       /* For bcast, enable/disable only if it's action is not
+        * packet replication, incase if action is replication
+        * then this PF's nixlf is removed from bcast replication
+        * list.
+        */
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc,
+                                        nixlf, NIXLF_BCAST_ENTRY);
+       bank = npc_get_bank(mcam, index);
+       *(u64 *)&action = rvu_read64(rvu, blkaddr,
+            NPC_AF_MCAMEX_BANKX_ACTION(index & (mcam->banksize - 1), bank));
+       if (action.op != NIX_RX_ACTIONOP_MCAST)
+               npc_enable_mcam_entry(rvu, mcam,
+                                     blkaddr, index, enable);
+       if (enable)
+               rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf);
+       else
                rvu_npc_disable_promisc_entry(rvu, pcifunc, nixlf);
-       }
+
+       rvu_npc_update_rxvlan(rvu, pcifunc, nixlf);
+}
+
+void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_default_entries(rvu, pcifunc, nixlf, false);
+}
+
+void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       npc_enadis_default_entries(rvu, pcifunc, nixlf, true);
+}
+
+void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return;
+
+       mutex_lock(&mcam->lock);
+
+       /* Disable and free all MCAM entries mapped to this 'pcifunc' */
+       npc_mcam_free_all_entries(rvu, mcam, blkaddr, pcifunc);
+
+       /* Free all MCAM counters mapped to this 'pcifunc' */
+       npc_mcam_free_all_counters(rvu, mcam, pcifunc);
+
+       mutex_unlock(&mcam->lock);
+
+       rvu_npc_disable_default_entries(rvu, pcifunc, nixlf);
 }
 
-#define LDATA_EXTRACT_CONFIG(intf, lid, ltype, ld, cfg) \
+#define SET_KEX_LD(intf, lid, ltype, ld, cfg)  \
        rvu_write64(rvu, blkaddr,                       \
                NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, ltype, ld), cfg)
 
-#define LDATA_FLAGS_CONFIG(intf, ld, flags, cfg)       \
+#define SET_KEX_LDFLAGS(intf, ld, flags, cfg)  \
        rvu_write64(rvu, blkaddr,                       \
                NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, flags), cfg)
 
+#define KEX_LD_CFG(bytesm1, hdr_ofs, ena, flags_ena, key_ofs)          \
+                       (((bytesm1) << 16) | ((hdr_ofs) << 8) | ((ena) << 7) | \
+                        ((flags_ena) << 6) | ((key_ofs) & 0x3F))
+
 static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr)
 {
        struct npc_mcam *mcam = &rvu->hw->mcam;
@@ -514,28 +670,171 @@ static void npc_config_ldata_extract(struct rvu *rvu, int blkaddr)
         */
        for (lid = 0; lid < lid_count; lid++) {
                for (ltype = 0; ltype < 16; ltype++) {
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, lid, ltype, 0, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, lid, ltype, 1, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_TX, lid, ltype, 0, 0ULL);
-                       LDATA_EXTRACT_CONFIG(NIX_INTF_TX, lid, ltype, 1, 0ULL);
-
-                       LDATA_FLAGS_CONFIG(NIX_INTF_RX, 0, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_RX, 1, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_TX, 0, ltype, 0ULL);
-                       LDATA_FLAGS_CONFIG(NIX_INTF_TX, 1, ltype, 0ULL);
+                       SET_KEX_LD(NIX_INTF_RX, lid, ltype, 0, 0ULL);
+                       SET_KEX_LD(NIX_INTF_RX, lid, ltype, 1, 0ULL);
+                       SET_KEX_LD(NIX_INTF_TX, lid, ltype, 0, 0ULL);
+                       SET_KEX_LD(NIX_INTF_TX, lid, ltype, 1, 0ULL);
+
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, 0, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, 1, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, 0, ltype, 0ULL);
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, 1, ltype, 0ULL);
                }
        }
 
-       /* If we plan to extract Outer IPv4 tuple for TCP/UDP pkts
-        * then 112bit key is not sufficient
-        */
        if (mcam->keysize != NPC_MCAM_KEY_X2)
                return;
 
-       /* Start placing extracted data/flags from 64bit onwards, for now */
-       /* Extract DMAC from the packet */
-       cfg = (0x05 << 16) | BIT_ULL(7) | NPC_PARSE_RESULT_DMAC_OFFSET;
-       LDATA_EXTRACT_CONFIG(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 0, cfg);
+       /* Default MCAM KEX profile */
+       /* Layer A: Ethernet: */
+
+       /* DMAC: 6 bytes, KW1[47:0] */
+       cfg = KEX_LD_CFG(0x05, 0x0, 0x1, 0x0, NPC_PARSE_RESULT_DMAC_OFFSET);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 0, cfg);
+
+       /* Ethertype: 2 bytes, KW0[47:32] */
+       cfg = KEX_LD_CFG(0x01, 0xc, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LA, NPC_LT_LA_ETHER, 1, cfg);
+
+       /* Layer B: Single VLAN (CTAG) */
+       /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */
+       cfg = KEX_LD_CFG(0x03, 0x0, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_CTAG, 0, cfg);
+
+       /* Layer B: Stacked VLAN (STAG|QinQ) */
+       /* CTAG VLAN[2..3] + Ethertype, 4 bytes, KW0[63:32] */
+       cfg = KEX_LD_CFG(0x03, 0x4, 0x1, 0x0, 0x4);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_STAG, 0, cfg);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LB, NPC_LT_LB_QINQ, 0, cfg);
+
+       /* Layer C: IPv4 */
+       /* SIP+DIP: 8 bytes, KW2[63:0] */
+       cfg = KEX_LD_CFG(0x07, 0xc, 0x1, 0x0, 0x10);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 0, cfg);
+       /* TOS: 1 byte, KW1[63:56] */
+       cfg = KEX_LD_CFG(0x0, 0x1, 0x1, 0x0, 0xf);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LC, NPC_LT_LC_IP, 1, cfg);
+
+       /* Layer D:UDP */
+       /* SPORT: 2 bytes, KW3[15:0] */
+       cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 0, cfg);
+       /* DPORT: 2 bytes, KW3[31:16] */
+       cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_UDP, 1, cfg);
+
+       /* Layer D:TCP */
+       /* SPORT: 2 bytes, KW3[15:0] */
+       cfg = KEX_LD_CFG(0x1, 0x0, 0x1, 0x0, 0x18);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 0, cfg);
+       /* DPORT: 2 bytes, KW3[31:16] */
+       cfg = KEX_LD_CFG(0x1, 0x2, 0x1, 0x0, 0x1a);
+       SET_KEX_LD(NIX_INTF_RX, NPC_LID_LD, NPC_LT_LD_TCP, 1, cfg);
+}
+
+static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr,
+                                    struct npc_mcam_kex *mkex)
+{
+       int lid, lt, ld, fl;
+
+       rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX),
+                   mkex->keyx_cfg[NIX_INTF_RX]);
+       rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX),
+                   mkex->keyx_cfg[NIX_INTF_TX]);
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++)
+               rvu_write64(rvu, blkaddr, NPC_AF_KEX_LDATAX_FLAGS_CFG(ld),
+                           mkex->kex_ld_flags[ld]);
+
+       for (lid = 0; lid < NPC_MAX_LID; lid++) {
+               for (lt = 0; lt < NPC_MAX_LT; lt++) {
+                       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+                               SET_KEX_LD(NIX_INTF_RX, lid, lt, ld,
+                                          mkex->intf_lid_lt_ld[NIX_INTF_RX]
+                                          [lid][lt][ld]);
+
+                               SET_KEX_LD(NIX_INTF_TX, lid, lt, ld,
+                                          mkex->intf_lid_lt_ld[NIX_INTF_TX]
+                                          [lid][lt][ld]);
+                       }
+               }
+       }
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+               for (fl = 0; fl < NPC_MAX_LFL; fl++) {
+                       SET_KEX_LDFLAGS(NIX_INTF_RX, ld, fl,
+                                       mkex->intf_ld_flags[NIX_INTF_RX]
+                                       [ld][fl]);
+
+                       SET_KEX_LDFLAGS(NIX_INTF_TX, ld, fl,
+                                       mkex->intf_ld_flags[NIX_INTF_TX]
+                                       [ld][fl]);
+               }
+       }
+}
+
+/* strtoull of "mkexprof" with base:36 */
+#define MKEX_SIGN      0x19bbfdbd15f
+#define MKEX_END_SIGN  0xdeadbeef
+
+static void npc_load_mkex_profile(struct rvu *rvu, int blkaddr)
+{
+       const char *mkex_profile = rvu->mkex_pfl_name;
+       struct device *dev = &rvu->pdev->dev;
+       void __iomem *mkex_prfl_addr = NULL;
+       struct npc_mcam_kex *mcam_kex;
+       u64 prfl_addr;
+       u64 prfl_sz;
+
+       /* If user not selected mkex profile */
+       if (!strncmp(mkex_profile, "default", MKEX_NAME_LEN))
+               goto load_default;
+
+       if (cgx_get_mkex_prfl_info(&prfl_addr, &prfl_sz))
+               goto load_default;
+
+       if (!prfl_addr || !prfl_sz)
+               goto load_default;
+
+       mkex_prfl_addr = ioremap_wc(prfl_addr, prfl_sz);
+       if (!mkex_prfl_addr)
+               goto load_default;
+
+       mcam_kex = (struct npc_mcam_kex *)mkex_prfl_addr;
+
+       while (((s64)prfl_sz > 0) && (mcam_kex->mkex_sign != MKEX_END_SIGN)) {
+               /* Compare with mkex mod_param name string */
+               if (mcam_kex->mkex_sign == MKEX_SIGN &&
+                   !strncmp(mcam_kex->name, mkex_profile, MKEX_NAME_LEN)) {
+                       /* Due to an errata (35786) in A0 pass silicon,
+                        * parse nibble enable configuration has to be
+                        * identical for both Rx and Tx interfaces.
+                        */
+                       if (is_rvu_9xxx_A0(rvu) &&
+                           mcam_kex->keyx_cfg[NIX_INTF_RX] !=
+                           mcam_kex->keyx_cfg[NIX_INTF_TX])
+                               goto load_default;
+
+                       /* Program selected mkex profile */
+                       npc_program_mkex_profile(rvu, blkaddr, mcam_kex);
+
+                       goto unmap;
+               }
+
+               mcam_kex++;
+               prfl_sz -= sizeof(struct npc_mcam_kex);
+       }
+       dev_warn(dev, "Failed to load requested profile: %s\n",
+                rvu->mkex_pfl_name);
+
+load_default:
+       dev_info(rvu->dev, "Using default mkex profile\n");
+       /* Config packet data and flags extraction into PARSE result */
+       npc_config_ldata_extract(rvu, blkaddr);
+
+unmap:
+       if (mkex_prfl_addr)
+               iounmap(mkex_prfl_addr);
 }
 
 static void npc_config_kpuaction(struct rvu *rvu, int blkaddr,
@@ -690,13 +989,14 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr)
 {
        int nixlf_count = rvu_get_nixlf_count(rvu);
        struct npc_mcam *mcam = &rvu->hw->mcam;
-       int rsvd;
+       int rsvd, err;
        u64 cfg;
 
        /* Get HW limits */
        cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST);
        mcam->banks = (cfg >> 44) & 0xF;
        mcam->banksize = (cfg >> 28) & 0xFFFF;
+       mcam->counters.max = (cfg >> 48) & 0xFFFF;
 
        /* Actual number of MCAM entries vary by entry size */
        cfg = (rvu_read64(rvu, blkaddr,
@@ -728,20 +1028,82 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr)
                return -ENOMEM;
        }
 
-       mcam->entries = mcam->total_entries - rsvd;
-       mcam->nixlf_offset = mcam->entries;
+       mcam->bmap_entries = mcam->total_entries - rsvd;
+       mcam->nixlf_offset = mcam->bmap_entries;
        mcam->pf_offset = mcam->nixlf_offset + nixlf_count;
 
-       spin_lock_init(&mcam->lock);
+       /* Allocate bitmaps for managing MCAM entries */
+       mcam->bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(mcam->bmap_entries),
+                                 sizeof(long), GFP_KERNEL);
+       if (!mcam->bmap)
+               return -ENOMEM;
+
+       mcam->bmap_reverse = devm_kcalloc(rvu->dev,
+                                         BITS_TO_LONGS(mcam->bmap_entries),
+                                         sizeof(long), GFP_KERNEL);
+       if (!mcam->bmap_reverse)
+               return -ENOMEM;
+
+       mcam->bmap_fcnt = mcam->bmap_entries;
+
+       /* Alloc memory for saving entry to RVU PFFUNC allocation mapping */
+       mcam->entry2pfvf_map = devm_kcalloc(rvu->dev, mcam->bmap_entries,
+                                           sizeof(u16), GFP_KERNEL);
+       if (!mcam->entry2pfvf_map)
+               return -ENOMEM;
+
+       /* Reserve 1/8th of MCAM entries at the bottom for low priority
+        * allocations and another 1/8th at the top for high priority
+        * allocations.
+        */
+       mcam->lprio_count = mcam->bmap_entries / 8;
+       if (mcam->lprio_count > BITS_PER_LONG)
+               mcam->lprio_count = round_down(mcam->lprio_count,
+                                              BITS_PER_LONG);
+       mcam->lprio_start = mcam->bmap_entries - mcam->lprio_count;
+       mcam->hprio_count = mcam->lprio_count;
+       mcam->hprio_end = mcam->hprio_count;
+
+       /* Allocate bitmap for managing MCAM counters and memory
+        * for saving counter to RVU PFFUNC allocation mapping.
+        */
+       err = rvu_alloc_bitmap(&mcam->counters);
+       if (err)
+               return err;
+
+       mcam->cntr2pfvf_map = devm_kcalloc(rvu->dev, mcam->counters.max,
+                                          sizeof(u16), GFP_KERNEL);
+       if (!mcam->cntr2pfvf_map)
+               goto free_mem;
+
+       /* Alloc memory for MCAM entry to counter mapping and for tracking
+        * counter's reference count.
+        */
+       mcam->entry2cntr_map = devm_kcalloc(rvu->dev, mcam->bmap_entries,
+                                           sizeof(u16), GFP_KERNEL);
+       if (!mcam->entry2cntr_map)
+               goto free_mem;
+
+       mcam->cntr_refcnt = devm_kcalloc(rvu->dev, mcam->counters.max,
+                                        sizeof(u16), GFP_KERNEL);
+       if (!mcam->cntr_refcnt)
+               goto free_mem;
+
+       mutex_init(&mcam->lock);
 
        return 0;
+
+free_mem:
+       kfree(mcam->counters.bmap);
+       return -ENOMEM;
 }
 
 int rvu_npc_init(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
        u64 keyz = NPC_MCAM_KEY_X2;
-       int blkaddr, err;
+       int blkaddr, entry, bank, err;
+       u64 cfg, nibble_ena;
 
        blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
        if (blkaddr < 0) {
@@ -749,6 +1111,14 @@ int rvu_npc_init(struct rvu *rvu)
                return -ENODEV;
        }
 
+       /* First disable all MCAM entries, to stop traffic towards NIXLFs */
+       cfg = rvu_read64(rvu, blkaddr, NPC_AF_CONST);
+       for (bank = 0; bank < ((cfg >> 44) & 0xF); bank++) {
+               for (entry = 0; entry < ((cfg >> 28) & 0xFFFF); entry++)
+                       rvu_write64(rvu, blkaddr,
+                                   NPC_AF_MCAMEX_BANKX_CFG(entry, bank), 0);
+       }
+
        /* Allocate resource bimap for pkind*/
        pkind->rsrc.max = (rvu_read64(rvu, blkaddr,
                                      NPC_AF_CONST1) >> 12) & 0xFF;
@@ -771,29 +1141,41 @@ int rvu_npc_init(struct rvu *rvu)
        rvu_write64(rvu, blkaddr, NPC_AF_PCK_DEF_OIP4,
                    (NPC_LID_LC << 8) | (NPC_LT_LC_IP << 4) | 0x0F);
 
+       /* Config Inner IPV4 NPC layer info */
+       rvu_write64(rvu, blkaddr, NPC_AF_PCK_DEF_IIP4,
+                   (NPC_LID_LF << 8) | (NPC_LT_LF_TU_IP << 4) | 0x0F);
+
        /* Enable below for Rx pkts.
         * - Outer IPv4 header checksum validation.
         * - Detect outer L2 broadcast address and set NPC_RESULT_S[L2M].
+        * - Inner IPv4 header checksum validation.
+        * - Set non zero checksum error code value
         */
        rvu_write64(rvu, blkaddr, NPC_AF_PCK_CFG,
                    rvu_read64(rvu, blkaddr, NPC_AF_PCK_CFG) |
-                   BIT_ULL(6) | BIT_ULL(2));
+                   BIT_ULL(32) | BIT_ULL(24) | BIT_ULL(6) |
+                   BIT_ULL(2) | BIT_ULL(1));
 
        /* Set RX and TX side MCAM search key size.
-        * Also enable parse key extract nibbles suchthat except
-        * layer E to H, rest of the key is included for MCAM search.
+        * LA..LD (ltype only) + Channel
         */
+       nibble_ena = 0x49247;
        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX),
-                   ((keyz & 0x3) << 32) | ((1ULL << 20) - 1));
+                       ((keyz & 0x3) << 32) | nibble_ena);
+       /* Due to an errata (35786) in A0 pass silicon, parse nibble enable
+        * configuration has to be identical for both Rx and Tx interfaces.
+        */
+       if (!is_rvu_9xxx_A0(rvu))
+               nibble_ena = (1ULL << 19) - 1;
        rvu_write64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_TX),
-                   ((keyz & 0x3) << 32) | ((1ULL << 20) - 1));
+                       ((keyz & 0x3) << 32) | nibble_ena);
 
        err = npc_mcam_rsrcs_init(rvu, blkaddr);
        if (err)
                return err;
 
-       /* Config packet data and flags extraction into PARSE result */
-       npc_config_ldata_extract(rvu, blkaddr);
+       /* Configure MKEX profile */
+       npc_load_mkex_profile(rvu, blkaddr);
 
        /* Set TX miss action to UCAST_DEFAULT i.e
         * transmit the packet on NIX LF SQ's default channel.
@@ -811,6 +1193,1020 @@ int rvu_npc_init(struct rvu *rvu)
 void rvu_npc_freemem(struct rvu *rvu)
 {
        struct npc_pkind *pkind = &rvu->hw->pkind;
+       struct npc_mcam *mcam = &rvu->hw->mcam;
 
        kfree(pkind->rsrc.bmap);
+       kfree(mcam->counters.bmap);
+       mutex_destroy(&mcam->lock);
+}
+
+static int npc_mcam_verify_entry(struct npc_mcam *mcam,
+                                u16 pcifunc, int entry)
+{
+       /* Verify if entry is valid and if it is indeed
+        * allocated to the requesting PFFUNC.
+        */
+       if (entry >= mcam->bmap_entries)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (pcifunc != mcam->entry2pfvf_map[entry])
+               return NPC_MCAM_PERM_DENIED;
+
+       return 0;
+}
+
+static int npc_mcam_verify_counter(struct npc_mcam *mcam,
+                                  u16 pcifunc, int cntr)
+{
+       /* Verify if counter is valid and if it is indeed
+        * allocated to the requesting PFFUNC.
+        */
+       if (cntr >= mcam->counters.max)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (pcifunc != mcam->cntr2pfvf_map[cntr])
+               return NPC_MCAM_PERM_DENIED;
+
+       return 0;
+}
+
+static void npc_map_mcam_entry_and_cntr(struct rvu *rvu, struct npc_mcam *mcam,
+                                       int blkaddr, u16 entry, u16 cntr)
+{
+       u16 index = entry & (mcam->banksize - 1);
+       u16 bank = npc_get_bank(mcam, entry);
+
+       /* Set mapping and increment counter's refcnt */
+       mcam->entry2cntr_map[entry] = cntr;
+       mcam->cntr_refcnt[cntr]++;
+       /* Enable stats */
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank),
+                   BIT_ULL(9) | cntr);
+}
+
+static void npc_unmap_mcam_entry_and_cntr(struct rvu *rvu,
+                                         struct npc_mcam *mcam,
+                                         int blkaddr, u16 entry, u16 cntr)
+{
+       u16 index = entry & (mcam->banksize - 1);
+       u16 bank = npc_get_bank(mcam, entry);
+
+       /* Remove mapping and reduce counter's refcnt */
+       mcam->entry2cntr_map[entry] = NPC_MCAM_INVALID_MAP;
+       mcam->cntr_refcnt[cntr]--;
+       /* Disable stats */
+       rvu_write64(rvu, blkaddr,
+                   NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank), 0x00);
+}
+
+/* Sets MCAM entry in bitmap as used. Update
+ * reverse bitmap too. Should be called with
+ * 'mcam->lock' held.
+ */
+static void npc_mcam_set_bit(struct npc_mcam *mcam, u16 index)
+{
+       u16 entry, rentry;
+
+       entry = index;
+       rentry = mcam->bmap_entries - index - 1;
+
+       __set_bit(entry, mcam->bmap);
+       __set_bit(rentry, mcam->bmap_reverse);
+       mcam->bmap_fcnt--;
+}
+
+/* Sets MCAM entry in bitmap as free. Update
+ * reverse bitmap too. Should be called with
+ * 'mcam->lock' held.
+ */
+static void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index)
+{
+       u16 entry, rentry;
+
+       entry = index;
+       rentry = mcam->bmap_entries - index - 1;
+
+       __clear_bit(entry, mcam->bmap);
+       __clear_bit(rentry, mcam->bmap_reverse);
+       mcam->bmap_fcnt++;
+}
+
+static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
+                                     int blkaddr, u16 pcifunc)
+{
+       u16 index, cntr;
+
+       /* Scan all MCAM entries and free the ones mapped to 'pcifunc' */
+       for (index = 0; index < mcam->bmap_entries; index++) {
+               if (mcam->entry2pfvf_map[index] == pcifunc) {
+                       mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
+                       /* Free the entry in bitmap */
+                       npc_mcam_clear_bit(mcam, index);
+                       /* Disable the entry */
+                       npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+
+                       /* Update entry2counter mapping */
+                       cntr = mcam->entry2cntr_map[index];
+                       if (cntr != NPC_MCAM_INVALID_MAP)
+                               npc_unmap_mcam_entry_and_cntr(rvu, mcam,
+                                                             blkaddr, index,
+                                                             cntr);
+               }
+       }
+}
+
+static void npc_mcam_free_all_counters(struct rvu *rvu, struct npc_mcam *mcam,
+                                      u16 pcifunc)
+{
+       u16 cntr;
+
+       /* Scan all MCAM counters and free the ones mapped to 'pcifunc' */
+       for (cntr = 0; cntr < mcam->counters.max; cntr++) {
+               if (mcam->cntr2pfvf_map[cntr] == pcifunc) {
+                       mcam->cntr2pfvf_map[cntr] = NPC_MCAM_INVALID_MAP;
+                       mcam->cntr_refcnt[cntr] = 0;
+                       rvu_free_rsrc(&mcam->counters, cntr);
+                       /* This API is expected to be called after freeing
+                        * MCAM entries, which inturn will remove
+                        * 'entry to counter' mapping.
+                        * No need to do it again.
+                        */
+               }
+       }
+}
+
+/* Find area of contiguous free entries of size 'nr'.
+ * If not found return max contiguous free entries available.
+ */
+static u16 npc_mcam_find_zero_area(unsigned long *map, u16 size, u16 start,
+                                  u16 nr, u16 *max_area)
+{
+       u16 max_area_start = 0;
+       u16 index, next, end;
+
+       *max_area = 0;
+
+again:
+       index = find_next_zero_bit(map, size, start);
+       if (index >= size)
+               return max_area_start;
+
+       end = ((index + nr) >= size) ? size : index + nr;
+       next = find_next_bit(map, end, index);
+       if (*max_area < (next - index)) {
+               *max_area = next - index;
+               max_area_start = index;
+       }
+
+       if (next < end) {
+               start = next + 1;
+               goto again;
+       }
+
+       return max_area_start;
+}
+
+/* Find number of free MCAM entries available
+ * within range i.e in between 'start' and 'end'.
+ */
+static u16 npc_mcam_get_free_count(unsigned long *map, u16 start, u16 end)
+{
+       u16 index, next;
+       u16 fcnt = 0;
+
+again:
+       if (start >= end)
+               return fcnt;
+
+       index = find_next_zero_bit(map, end, start);
+       if (index >= end)
+               return fcnt;
+
+       next = find_next_bit(map, end, index);
+       if (next <= end) {
+               fcnt += next - index;
+               start = next + 1;
+               goto again;
+       }
+
+       fcnt += end - index;
+       return fcnt;
+}
+
+static void
+npc_get_mcam_search_range_priority(struct npc_mcam *mcam,
+                                  struct npc_mcam_alloc_entry_req *req,
+                                  u16 *start, u16 *end, bool *reverse)
+{
+       u16 fcnt;
+
+       if (req->priority == NPC_MCAM_HIGHER_PRIO)
+               goto hprio;
+
+       /* For a low priority entry allocation
+        * - If reference entry is not in hprio zone then
+        *      search range: ref_entry to end.
+        * - If reference entry is in hprio zone and if
+        *   request can be accomodated in non-hprio zone then
+        *      search range: 'start of middle zone' to 'end'
+        * - else search in reverse, so that less number of hprio
+        *   zone entries are allocated.
+        */
+
+       *reverse = false;
+       *start = req->ref_entry + 1;
+       *end = mcam->bmap_entries;
+
+       if (req->ref_entry >= mcam->hprio_end)
+               return;
+
+       fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                      mcam->hprio_end, mcam->bmap_entries);
+       if (fcnt > req->count)
+               *start = mcam->hprio_end;
+       else
+               *reverse = true;
+       return;
+
+hprio:
+       /* For a high priority entry allocation, search is always
+        * in reverse to preserve hprio zone entries.
+        * - If reference entry is not in lprio zone then
+        *      search range: 0 to ref_entry.
+        * - If reference entry is in lprio zone and if
+        *   request can be accomodated in middle zone then
+        *      search range: 'hprio_end' to 'lprio_start'
+        */
+
+       *reverse = true;
+       *start = 0;
+       *end = req->ref_entry;
+
+       if (req->ref_entry <= mcam->lprio_start)
+               return;
+
+       fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                      mcam->hprio_end, mcam->lprio_start);
+       if (fcnt < req->count)
+               return;
+       *start = mcam->hprio_end;
+       *end = mcam->lprio_start;
+}
+
+static int npc_mcam_alloc_entries(struct npc_mcam *mcam, u16 pcifunc,
+                                 struct npc_mcam_alloc_entry_req *req,
+                                 struct npc_mcam_alloc_entry_rsp *rsp)
+{
+       u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
+       u16 fcnt, hp_fcnt, lp_fcnt;
+       u16 start, end, index;
+       int entry, next_start;
+       bool reverse = false;
+       unsigned long *bmap;
+       u16 max_contig;
+
+       mutex_lock(&mcam->lock);
+
+       /* Check if there are any free entries */
+       if (!mcam->bmap_fcnt) {
+               mutex_unlock(&mcam->lock);
+               return NPC_MCAM_ALLOC_FAILED;
+       }
+
+       /* MCAM entries are divided into high priority, middle and
+        * low priority zones. Idea is to not allocate top and lower
+        * most entries as much as possible, this is to increase
+        * probability of honouring priority allocation requests.
+        *
+        * Two bitmaps are used for mcam entry management,
+        * mcam->bmap for forward search i.e '0 to mcam->bmap_entries'.
+        * mcam->bmap_reverse for reverse search i.e 'mcam->bmap_entries to 0'.
+        *
+        * Reverse bitmap is used to allocate entries
+        * - when a higher priority entry is requested
+        * - when available free entries are less.
+        * Lower priority ones out of avaialble free entries are always
+        * chosen when 'high vs low' question arises.
+        */
+
+       /* Get the search range for priority allocation request */
+       if (req->priority) {
+               npc_get_mcam_search_range_priority(mcam, req,
+                                                  &start, &end, &reverse);
+               goto alloc;
+       }
+
+       /* Find out the search range for non-priority allocation request
+        *
+        * Get MCAM free entry count in middle zone.
+        */
+       lp_fcnt = npc_mcam_get_free_count(mcam->bmap,
+                                         mcam->lprio_start,
+                                         mcam->bmap_entries);
+       hp_fcnt = npc_mcam_get_free_count(mcam->bmap, 0, mcam->hprio_end);
+       fcnt = mcam->bmap_fcnt - lp_fcnt - hp_fcnt;
+
+       /* Check if request can be accomodated in the middle zone */
+       if (fcnt > req->count) {
+               start = mcam->hprio_end;
+               end = mcam->lprio_start;
+       } else if ((fcnt + (hp_fcnt / 2) + (lp_fcnt / 2)) > req->count) {
+               /* Expand search zone from half of hprio zone to
+                * half of lprio zone.
+                */
+               start = mcam->hprio_end / 2;
+               end = mcam->bmap_entries - (mcam->lprio_count / 2);
+               reverse = true;
+       } else {
+               /* Not enough free entries, search all entries in reverse,
+                * so that low priority ones will get used up.
+                */
+               reverse = true;
+               start = 0;
+               end = mcam->bmap_entries;
+       }
+
+alloc:
+       if (reverse) {
+               bmap = mcam->bmap_reverse;
+               start = mcam->bmap_entries - start;
+               end = mcam->bmap_entries - end;
+               index = start;
+               start = end;
+               end = index;
+       } else {
+               bmap = mcam->bmap;
+       }
+
+       if (req->contig) {
+               /* Allocate requested number of contiguous entries, if
+                * unsuccessful find max contiguous entries available.
+                */
+               index = npc_mcam_find_zero_area(bmap, end, start,
+                                               req->count, &max_contig);
+               rsp->count = max_contig;
+               if (reverse)
+                       rsp->entry = mcam->bmap_entries - index - max_contig;
+               else
+                       rsp->entry = index;
+       } else {
+               /* Allocate requested number of non-contiguous entries,
+                * if unsuccessful allocate as many as possible.
+                */
+               rsp->count = 0;
+               next_start = start;
+               for (entry = 0; entry < req->count; entry++) {
+                       index = find_next_zero_bit(bmap, end, next_start);
+                       if (index >= end)
+                               break;
+
+                       next_start = start + (index - start) + 1;
+
+                       /* Save the entry's index */
+                       if (reverse)
+                               index = mcam->bmap_entries - index - 1;
+                       entry_list[entry] = index;
+                       rsp->count++;
+               }
+       }
+
+       /* If allocating requested no of entries is unsucessful,
+        * expand the search range to full bitmap length and retry.
+        */
+       if (!req->priority && (rsp->count < req->count) &&
+           ((end - start) != mcam->bmap_entries)) {
+               reverse = true;
+               start = 0;
+               end = mcam->bmap_entries;
+               goto alloc;
+       }
+
+       /* For priority entry allocation requests, if allocation is
+        * failed then expand search to max possible range and retry.
+        */
+       if (req->priority && rsp->count < req->count) {
+               if (req->priority == NPC_MCAM_LOWER_PRIO &&
+                   (start != (req->ref_entry + 1))) {
+                       start = req->ref_entry + 1;
+                       end = mcam->bmap_entries;
+                       reverse = false;
+                       goto alloc;
+               } else if ((req->priority == NPC_MCAM_HIGHER_PRIO) &&
+                          ((end - start) != req->ref_entry)) {
+                       start = 0;
+                       end = req->ref_entry;
+                       reverse = true;
+                       goto alloc;
+               }
+       }
+
+       /* Copy MCAM entry indices into mbox response entry_list.
+        * Requester always expects indices in ascending order, so
+        * so reverse the list if reverse bitmap is used for allocation.
+        */
+       if (!req->contig && rsp->count) {
+               index = 0;
+               for (entry = rsp->count - 1; entry >= 0; entry--) {
+                       if (reverse)
+                               rsp->entry_list[index++] = entry_list[entry];
+                       else
+                               rsp->entry_list[entry] = entry_list[entry];
+               }
+       }
+
+       /* Mark the allocated entries as used and set nixlf mapping */
+       for (entry = 0; entry < rsp->count; entry++) {
+               index = req->contig ?
+                       (rsp->entry + entry) : rsp->entry_list[entry];
+               npc_mcam_set_bit(mcam, index);
+               mcam->entry2pfvf_map[index] = pcifunc;
+               mcam->entry2cntr_map[index] = NPC_MCAM_INVALID_MAP;
+       }
+
+       /* Update available free count in mbox response */
+       rsp->free_count = mcam->bmap_fcnt;
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu,
+                                         struct npc_mcam_alloc_entry_req *req,
+                                         struct npc_mcam_alloc_entry_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       rsp->entry = NPC_MCAM_ENTRY_INVALID;
+       rsp->free_count = 0;
+
+       /* Check if ref_entry is within range */
+       if (req->priority && req->ref_entry >= mcam->bmap_entries)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* ref_entry can't be '0' if requested priority is high.
+        * Can't be last entry if requested priority is low.
+        */
+       if ((!req->ref_entry && req->priority == NPC_MCAM_HIGHER_PRIO) ||
+           ((req->ref_entry == (mcam->bmap_entries - 1)) &&
+            req->priority == NPC_MCAM_LOWER_PRIO))
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Since list of allocated indices needs to be sent to requester,
+        * max number of non-contiguous entries per mbox msg is limited.
+        */
+       if (!req->contig && req->count > NPC_MAX_NONCONTIG_ENTRIES)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Alloc request from PFFUNC with no NIXLF attached should be denied */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_ALLOC_DENIED;
+
+       return npc_mcam_alloc_entries(mcam, pcifunc, req, rsp);
+}
+
+int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
+                                        struct npc_mcam_free_entry_req *req,
+                                        struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc = 0;
+       u16 cntr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Free request from PFFUNC with no NIXLF attached, ignore */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+
+       if (req->all)
+               goto free_all;
+
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       if (rc)
+               goto exit;
+
+       mcam->entry2pfvf_map[req->entry] = 0;
+       npc_mcam_clear_bit(mcam, req->entry);
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, false);
+
+       /* Update entry2counter mapping */
+       cntr = mcam->entry2cntr_map[req->entry];
+       if (cntr != NPC_MCAM_INVALID_MAP)
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             req->entry, cntr);
+
+       goto exit;
+
+free_all:
+       /* Free up all entries allocated to requesting PFFUNC */
+       npc_mcam_free_all_entries(rvu, mcam, blkaddr, pcifunc);
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_write_entry(struct rvu *rvu,
+                                         struct npc_mcam_write_entry_req *req,
+                                         struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       if (rc)
+               goto exit;
+
+       if (req->set_cntr &&
+           npc_mcam_verify_counter(mcam, pcifunc, req->cntr)) {
+               rc = NPC_MCAM_INVALID_REQ;
+               goto exit;
+       }
+
+       if (req->intf != NIX_INTF_RX && req->intf != NIX_INTF_TX) {
+               rc = NPC_MCAM_INVALID_REQ;
+               goto exit;
+       }
+
+       npc_config_mcam_entry(rvu, mcam, blkaddr, req->entry, req->intf,
+                             &req->entry_data, req->enable_entry);
+
+       if (req->set_cntr)
+               npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                           req->entry, req->cntr);
+
+       rc = 0;
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_ena_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       mutex_unlock(&mcam->lock);
+       if (rc)
+               return rc;
+
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, true);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_dis_entry(struct rvu *rvu,
+                                       struct npc_mcam_ena_dis_entry_req *req,
+                                       struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
+       mutex_unlock(&mcam->lock);
+       if (rc)
+               return rc;
+
+       npc_enable_mcam_entry(rvu, mcam, blkaddr, req->entry, false);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
+                                         struct npc_mcam_shift_entry_req *req,
+                                         struct npc_mcam_shift_entry_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 old_entry, new_entry;
+       u16 index, cntr;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (req->shift_count > NPC_MCAM_MAX_SHIFTS)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       for (index = 0; index < req->shift_count; index++) {
+               old_entry = req->curr_entry[index];
+               new_entry = req->new_entry[index];
+
+               /* Check if both old and new entries are valid and
+                * does belong to this PFFUNC or not.
+                */
+               rc = npc_mcam_verify_entry(mcam, pcifunc, old_entry);
+               if (rc)
+                       break;
+
+               rc = npc_mcam_verify_entry(mcam, pcifunc, new_entry);
+               if (rc)
+                       break;
+
+               /* new_entry should not have a counter mapped */
+               if (mcam->entry2cntr_map[new_entry] != NPC_MCAM_INVALID_MAP) {
+                       rc = NPC_MCAM_PERM_DENIED;
+                       break;
+               }
+
+               /* Disable the new_entry */
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false);
+
+               /* Copy rule from old entry to new entry */
+               npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry);
+
+               /* Copy counter mapping, if any */
+               cntr = mcam->entry2cntr_map[old_entry];
+               if (cntr != NPC_MCAM_INVALID_MAP) {
+                       npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                                     old_entry, cntr);
+                       npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                                   new_entry, cntr);
+               }
+
+               /* Enable new_entry and disable old_entry */
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, true);
+               npc_enable_mcam_entry(rvu, mcam, blkaddr, old_entry, false);
+       }
+
+       /* If shift has failed then report the failed index */
+       if (index != req->shift_count) {
+               rc = NPC_MCAM_PERM_DENIED;
+               rsp->failed_entry_idx = index;
+       }
+
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_counter(struct rvu *rvu,
+                       struct npc_mcam_alloc_counter_req *req,
+                       struct npc_mcam_alloc_counter_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 pcifunc = req->hdr.pcifunc;
+       u16 max_contig, cntr;
+       int blkaddr, index;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* If the request is from a PFFUNC with no NIXLF attached, ignore */
+       if (!is_nixlf_attached(rvu, pcifunc))
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Since list of allocated counter IDs needs to be sent to requester,
+        * max number of non-contiguous counters per mbox msg is limited.
+        */
+       if (!req->contig && req->count > NPC_MAX_NONCONTIG_COUNTERS)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+
+       /* Check if unused counters are available or not */
+       if (!rvu_rsrc_free_count(&mcam->counters)) {
+               mutex_unlock(&mcam->lock);
+               return NPC_MCAM_ALLOC_FAILED;
+       }
+
+       rsp->count = 0;
+
+       if (req->contig) {
+               /* Allocate requested number of contiguous counters, if
+                * unsuccessful find max contiguous entries available.
+                */
+               index = npc_mcam_find_zero_area(mcam->counters.bmap,
+                                               mcam->counters.max, 0,
+                                               req->count, &max_contig);
+               rsp->count = max_contig;
+               rsp->cntr = index;
+               for (cntr = index; cntr < (index + max_contig); cntr++) {
+                       __set_bit(cntr, mcam->counters.bmap);
+                       mcam->cntr2pfvf_map[cntr] = pcifunc;
+               }
+       } else {
+               /* Allocate requested number of non-contiguous counters,
+                * if unsuccessful allocate as many as possible.
+                */
+               for (cntr = 0; cntr < req->count; cntr++) {
+                       index = rvu_alloc_rsrc(&mcam->counters);
+                       if (index < 0)
+                               break;
+                       rsp->cntr_list[cntr] = index;
+                       rsp->count++;
+                       mcam->cntr2pfvf_map[index] = pcifunc;
+               }
+       }
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_free_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 index, entry = 0;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       if (err) {
+               mutex_unlock(&mcam->lock);
+               return err;
+       }
+
+       /* Mark counter as free/unused */
+       mcam->cntr2pfvf_map[req->cntr] = NPC_MCAM_INVALID_MAP;
+       rvu_free_rsrc(&mcam->counters, req->cntr);
+
+       /* Disable all MCAM entry's stats which are using this counter */
+       while (entry < mcam->bmap_entries) {
+               if (!mcam->cntr_refcnt[req->cntr])
+                       break;
+
+               index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry);
+               if (index >= mcam->bmap_entries)
+                       break;
+               if (mcam->entry2cntr_map[index] != req->cntr)
+                       continue;
+
+               entry = index + 1;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             index, req->cntr);
+       }
+
+       mutex_unlock(&mcam->lock);
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_unmap_counter(struct rvu *rvu,
+               struct npc_mcam_unmap_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 index, entry = 0;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       rc = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       if (rc)
+               goto exit;
+
+       /* Unmap the MCAM entry and counter */
+       if (!req->all) {
+               rc = npc_mcam_verify_entry(mcam, req->hdr.pcifunc, req->entry);
+               if (rc)
+                       goto exit;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             req->entry, req->cntr);
+               goto exit;
+       }
+
+       /* Disable all MCAM entry's stats which are using this counter */
+       while (entry < mcam->bmap_entries) {
+               if (!mcam->cntr_refcnt[req->cntr])
+                       break;
+
+               index = find_next_bit(mcam->bmap, mcam->bmap_entries, entry);
+               if (index >= mcam->bmap_entries)
+                       break;
+               if (mcam->entry2cntr_map[index] != req->cntr)
+                       continue;
+
+               entry = index + 1;
+               npc_unmap_mcam_entry_and_cntr(rvu, mcam, blkaddr,
+                                             index, req->cntr);
+       }
+exit:
+       mutex_unlock(&mcam->lock);
+       return rc;
+}
+
+int rvu_mbox_handler_npc_mcam_clear_counter(struct rvu *rvu,
+               struct npc_mcam_oper_counter_req *req, struct msg_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       mutex_unlock(&mcam->lock);
+       if (err)
+               return err;
+
+       rvu_write64(rvu, blkaddr, NPC_AF_MATCH_STATX(req->cntr), 0x00);
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_counter_stats(struct rvu *rvu,
+                       struct npc_mcam_oper_counter_req *req,
+                       struct npc_mcam_oper_counter_rsp *rsp)
+{
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, err;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       mutex_lock(&mcam->lock);
+       err = npc_mcam_verify_counter(mcam, req->hdr.pcifunc, req->cntr);
+       mutex_unlock(&mcam->lock);
+       if (err)
+               return err;
+
+       rsp->stat = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(req->cntr));
+       rsp->stat &= BIT_ULL(48) - 1;
+
+       return 0;
+}
+
+int rvu_mbox_handler_npc_mcam_alloc_and_write_entry(struct rvu *rvu,
+                         struct npc_mcam_alloc_and_write_entry_req *req,
+                         struct npc_mcam_alloc_and_write_entry_rsp *rsp)
+{
+       struct npc_mcam_alloc_counter_req cntr_req;
+       struct npc_mcam_alloc_counter_rsp cntr_rsp;
+       struct npc_mcam_alloc_entry_req entry_req;
+       struct npc_mcam_alloc_entry_rsp entry_rsp;
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       u16 entry = NPC_MCAM_ENTRY_INVALID;
+       u16 cntr = NPC_MCAM_ENTRY_INVALID;
+       int blkaddr, rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NPC_MCAM_INVALID_REQ;
+
+       if (req->intf != NIX_INTF_RX && req->intf != NIX_INTF_TX)
+               return NPC_MCAM_INVALID_REQ;
+
+       /* Try to allocate a MCAM entry */
+       entry_req.hdr.pcifunc = req->hdr.pcifunc;
+       entry_req.contig = true;
+       entry_req.priority = req->priority;
+       entry_req.ref_entry = req->ref_entry;
+       entry_req.count = 1;
+
+       rc = rvu_mbox_handler_npc_mcam_alloc_entry(rvu,
+                                                  &entry_req, &entry_rsp);
+       if (rc)
+               return rc;
+
+       if (!entry_rsp.count)
+               return NPC_MCAM_ALLOC_FAILED;
+
+       entry = entry_rsp.entry;
+
+       if (!req->alloc_cntr)
+               goto write_entry;
+
+       /* Now allocate counter */
+       cntr_req.hdr.pcifunc = req->hdr.pcifunc;
+       cntr_req.contig = true;
+       cntr_req.count = 1;
+
+       rc = rvu_mbox_handler_npc_mcam_alloc_counter(rvu, &cntr_req, &cntr_rsp);
+       if (rc) {
+               /* Free allocated MCAM entry */
+               mutex_lock(&mcam->lock);
+               mcam->entry2pfvf_map[entry] = 0;
+               npc_mcam_clear_bit(mcam, entry);
+               mutex_unlock(&mcam->lock);
+               return rc;
+       }
+
+       cntr = cntr_rsp.cntr;
+
+write_entry:
+       mutex_lock(&mcam->lock);
+       npc_config_mcam_entry(rvu, mcam, blkaddr, entry, req->intf,
+                             &req->entry_data, req->enable_entry);
+
+       if (req->alloc_cntr)
+               npc_map_mcam_entry_and_cntr(rvu, mcam, blkaddr, entry, cntr);
+       mutex_unlock(&mcam->lock);
+
+       rsp->entry = entry;
+       rsp->cntr = cntr;
+
+       return 0;
+}
+
+#define GET_KEX_CFG(intf) \
+       rvu_read64(rvu, BLKADDR_NPC, NPC_AF_INTFX_KEX_CFG(intf))
+
+#define GET_KEX_FLAGS(ld) \
+       rvu_read64(rvu, BLKADDR_NPC, NPC_AF_KEX_LDATAX_FLAGS_CFG(ld))
+
+#define GET_KEX_LD(intf, lid, lt, ld)  \
+       rvu_read64(rvu, BLKADDR_NPC,    \
+               NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, lt, ld))
+
+#define GET_KEX_LDFLAGS(intf, ld, fl)  \
+       rvu_read64(rvu, BLKADDR_NPC,    \
+               NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, fl))
+
+int rvu_mbox_handler_npc_get_kex_cfg(struct rvu *rvu, struct msg_req *req,
+                                    struct npc_get_kex_cfg_rsp *rsp)
+{
+       int lid, lt, ld, fl;
+
+       rsp->rx_keyx_cfg = GET_KEX_CFG(NIX_INTF_RX);
+       rsp->tx_keyx_cfg = GET_KEX_CFG(NIX_INTF_TX);
+       for (lid = 0; lid < NPC_MAX_LID; lid++) {
+               for (lt = 0; lt < NPC_MAX_LT; lt++) {
+                       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+                               rsp->intf_lid_lt_ld[NIX_INTF_RX][lid][lt][ld] =
+                                       GET_KEX_LD(NIX_INTF_RX, lid, lt, ld);
+                               rsp->intf_lid_lt_ld[NIX_INTF_TX][lid][lt][ld] =
+                                       GET_KEX_LD(NIX_INTF_TX, lid, lt, ld);
+                       }
+               }
+       }
+       for (ld = 0; ld < NPC_MAX_LD; ld++)
+               rsp->kex_ld_flags[ld] = GET_KEX_FLAGS(ld);
+
+       for (ld = 0; ld < NPC_MAX_LD; ld++) {
+               for (fl = 0; fl < NPC_MAX_LFL; fl++) {
+                       rsp->intf_ld_flags[NIX_INTF_RX][ld][fl] =
+                                       GET_KEX_LDFLAGS(NIX_INTF_RX, ld, fl);
+                       rsp->intf_ld_flags[NIX_INTF_TX][ld][fl] =
+                                       GET_KEX_LDFLAGS(NIX_INTF_TX, ld, fl);
+               }
+       }
+       memcpy(rsp->mkex_pfl_name, rvu->mkex_pfl_name, MKEX_NAME_LEN);
+       return 0;
+}
+
+int rvu_npc_update_rxvlan(struct rvu *rvu, u16 pcifunc, int nixlf)
+{
+       struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
+       struct npc_mcam *mcam = &rvu->hw->mcam;
+       int blkaddr, index;
+       bool enable;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+       if (blkaddr < 0)
+               return NIX_AF_ERR_AF_LF_INVALID;
+
+       if (!pfvf->rxvlan)
+               return 0;
+
+       index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf,
+                                        NIXLF_UCAST_ENTRY);
+       pfvf->entry.action = npc_get_mcam_action(rvu, mcam, blkaddr, index);
+       enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, index);
+       npc_config_mcam_entry(rvu, mcam, blkaddr, pfvf->rxvlan_index,
+                             NIX_INTF_RX, &pfvf->entry, enable);
+
+       return 0;
 }
index 9c08c36..04fd1f1 100644 (file)
@@ -3732,19 +3732,7 @@ static int skge_debug_show(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int skge_debug_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, skge_debug_show, inode->i_private);
-}
-
-static const struct file_operations skge_debug_fops = {
-       .owner          = THIS_MODULE,
-       .open           = skge_debug_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(skge_debug);
 
 /*
  * Use network device events to create/remove/rename
index 697d9b3..f3a5fa8 100644 (file)
@@ -2485,13 +2485,11 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
                skb->ip_summed = re->skb->ip_summed;
                skb->csum = re->skb->csum;
                skb_copy_hash(skb, re->skb);
-               skb->vlan_proto = re->skb->vlan_proto;
-               skb->vlan_tci = re->skb->vlan_tci;
+               __vlan_hwaccel_copy_tag(skb, re->skb);
 
                pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
                                               length, PCI_DMA_FROMDEVICE);
-               re->skb->vlan_proto = 0;
-               re->skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(re->skb);
                skb_clear_hash(re->skb);
                re->skb->ip_summed = CHECKSUM_NONE;
                skb_put(skb, length);
@@ -4623,19 +4621,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
        napi_enable(&hw->napi);
        return 0;
 }
-
-static int sky2_debug_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, sky2_debug_show, inode->i_private);
-}
-
-static const struct file_operations sky2_debug_fops = {
-       .owner          = THIS_MODULE,
-       .open           = sky2_debug_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(sky2_debug);
 
 /*
  * Use network device events to create/remove/rename
index 7dbfdac..399f565 100644 (file)
@@ -243,7 +243,7 @@ static void mtk_phy_link_adjust(struct net_device *dev)
                if (dev->phydev->asym_pause)
                        rmt_adv |= LPA_PAUSE_ASYM;
 
-               lcl_adv = ethtool_adv_to_lcl_adv_t(dev->phydev->advertising);
+               lcl_adv = linkmode_adv_to_lcl_adv_t(dev->phydev->advertising);
                flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
 
                if (flowctrl & FLOW_CTRL_TX)
@@ -353,8 +353,9 @@ static int mtk_phy_connect(struct net_device *dev)
 
        phy_set_max_speed(dev->phydev, SPEED_1000);
        phy_support_asym_pause(dev->phydev);
-       dev->phydev->advertising = dev->phydev->supported |
-                                   ADVERTISED_Autoneg;
+       linkmode_copy(dev->phydev->advertising, dev->phydev->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                        dev->phydev->advertising);
        phy_start_aneg(dev->phydev);
 
        of_node_put(np);
index d8e9a32..db909b6 100644 (file)
@@ -144,9 +144,9 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type)
 }
 
 static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
-                        int cq_num)
+                        int cq_num, u8 opmod)
 {
-       return mlx4_cmd(dev, mailbox->dma, cq_num, 0,
+       return mlx4_cmd(dev, mailbox->dma, cq_num, opmod,
                        MLX4_CMD_SW2HW_CQ, MLX4_CMD_TIME_CLASS_A,
                        MLX4_CMD_WRAPPED);
 }
@@ -287,11 +287,61 @@ static void mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn)
                __mlx4_cq_free_icm(dev, cqn);
 }
 
+static int mlx4_init_user_cqes(void *buf, int entries, int cqe_size)
+{
+       int entries_per_copy = PAGE_SIZE / cqe_size;
+       void *init_ents;
+       int err = 0;
+       int i;
+
+       init_ents = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!init_ents)
+               return -ENOMEM;
+
+       /* Populate a list of CQ entries to reduce the number of
+        * copy_to_user calls. 0xcc is the initialization value
+        * required by the FW.
+        */
+       memset(init_ents, 0xcc, PAGE_SIZE);
+
+       if (entries_per_copy < entries) {
+               for (i = 0; i < entries / entries_per_copy; i++) {
+                       err = copy_to_user(buf, init_ents, PAGE_SIZE);
+                       if (err)
+                               goto out;
+
+                       buf += PAGE_SIZE;
+               }
+       } else {
+               err = copy_to_user(buf, init_ents, entries * cqe_size);
+       }
+
+out:
+       kfree(init_ents);
+
+       return err;
+}
+
+static void mlx4_init_kernel_cqes(struct mlx4_buf *buf,
+                                 int entries,
+                                 int cqe_size)
+{
+       int i;
+
+       if (buf->nbufs == 1)
+               memset(buf->direct.buf, 0xcc, entries * cqe_size);
+       else
+               for (i = 0; i < buf->npages; i++)
+                       memset(buf->page_list[i].buf, 0xcc,
+                              1UL << buf->page_shift);
+}
+
 int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
                  struct mlx4_mtt *mtt, struct mlx4_uar *uar, u64 db_rec,
                  struct mlx4_cq *cq, unsigned vector, int collapsed,
-                 int timestamp_en)
+                 int timestamp_en, void *buf_addr, bool user_cq)
 {
+       bool sw_cq_init = dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_SW_CQ_INIT;
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct mlx4_cq_table *cq_table = &priv->cq_table;
        struct mlx4_cmd_mailbox *mailbox;
@@ -336,7 +386,20 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
        cq_context->mtt_base_addr_l = cpu_to_be32(mtt_addr & 0xffffffff);
        cq_context->db_rec_addr     = cpu_to_be64(db_rec);
 
-       err = mlx4_SW2HW_CQ(dev, mailbox, cq->cqn);
+       if (sw_cq_init) {
+               if (user_cq) {
+                       err = mlx4_init_user_cqes(buf_addr, nent,
+                                                 dev->caps.cqe_size);
+                       if (err)
+                               sw_cq_init = false;
+               } else {
+                       mlx4_init_kernel_cqes(buf_addr, nent,
+                                             dev->caps.cqe_size);
+               }
+       }
+
+       err = mlx4_SW2HW_CQ(dev, mailbox, cq->cqn, sw_cq_init);
+
        mlx4_free_cmd_mailbox(dev, mailbox);
        if (err)
                goto err_radix;
index 1e487ac..062a88f 100644 (file)
@@ -143,7 +143,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
        cq->mcq.usage = MLX4_RES_USAGE_DRIVER;
        err = mlx4_cq_alloc(mdev->dev, cq->size, &cq->wqres.mtt,
                            &mdev->priv_uar, cq->wqres.db.dma, &cq->mcq,
-                           cq->vector, 0, timestamp_en);
+                           cq->vector, 0, timestamp_en, &cq->wqres.buf, false);
        if (err)
                goto free_eq;
 
index db00bf1..fd09ba9 100644 (file)
@@ -875,7 +875,7 @@ csum_none:
                        skb->data_len = length;
                        napi_gro_frags(&cq->napi);
                } else {
-                       skb->vlan_tci = 0;
+                       __vlan_hwaccel_clear_tag(skb);
                        skb_clear_hash(skb);
                }
 next:
index babcfd9..7df728f 100644 (file)
@@ -166,6 +166,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags)
                [37] = "sl to vl mapping table change event support",
                [38] = "user MAC support",
                [39] = "Report driver version to FW support",
+               [40] = "SW CQ initialization support",
        };
        int i;
 
@@ -1098,6 +1099,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FSM;
        if (field32 & (1 << 21))
                dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_80_VFS;
+       if (field32 & (1 << 23))
+               dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_SW_CQ_INIT;
 
        for (i = 1; i <= dev_cap->num_ports; i++) {
                err = mlx4_QUERY_PORT(dev, i, dev_cap->port_cap + i);
index 6a04603..bdb8dd1 100644 (file)
@@ -63,7 +63,7 @@ struct workqueue_struct *mlx4_wq;
 
 #ifdef CONFIG_MLX4_DEBUG
 
-int mlx4_debug_level = 0;
+int mlx4_debug_level; /* 0 by default */
 module_param_named(debug_level, mlx4_debug_level, int, 0644);
 MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0");
 
@@ -83,7 +83,7 @@ MODULE_PARM_DESC(msi_x, "0 - don't use MSI-X, 1 - use MSI-X, >1 - limit number o
 
 static uint8_t num_vfs[3] = {0, 0, 0};
 static int num_vfs_argc;
-module_param_array(num_vfs, byte , &num_vfs_argc, 0444);
+module_param_array(num_vfs, byte, &num_vfs_argc, 0444);
 MODULE_PARM_DESC(num_vfs, "enable #num_vfs functions if num_vfs > 0\n"
                          "num_vfs=port1,port2,port1+2");
 
@@ -313,7 +313,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
                for (i = 0; i < dev->caps.num_ports - 1; i++) {
                        if (port_type[i] != port_type[i + 1]) {
                                mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
-                               return -EINVAL;
+                               return -EOPNOTSUPP;
                        }
                }
        }
@@ -322,7 +322,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
                if (!(port_type[i] & dev->caps.supported_type[i+1])) {
                        mlx4_err(dev, "Requested port type for port %d is not supported on this HCA\n",
                                 i + 1);
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
                }
        }
        return 0;
@@ -1188,8 +1188,7 @@ static int __set_port_type(struct mlx4_port_info *info,
                mlx4_err(mdev,
                         "Requested port type for port %d is not supported on this HCA\n",
                         info->port);
-               err = -EINVAL;
-               goto err_sup;
+               return -EOPNOTSUPP;
        }
 
        mlx4_stop_sense(mdev);
@@ -1211,7 +1210,7 @@ static int __set_port_type(struct mlx4_port_info *info,
                for (i = 1; i <= mdev->caps.num_ports; i++) {
                        if (mdev->caps.possible_type[i] == MLX4_PORT_TYPE_AUTO) {
                                mdev->caps.possible_type[i] = mdev->caps.port_type[i];
-                               err = -EINVAL;
+                               err = -EOPNOTSUPP;
                        }
                }
        }
@@ -1237,7 +1236,7 @@ static int __set_port_type(struct mlx4_port_info *info,
 out:
        mlx4_start_sense(mdev);
        mutex_unlock(&priv->port_mutex);
-err_sup:
+
        return err;
 }
 
@@ -3252,7 +3251,7 @@ disable_sriov:
 free_mem:
        dev->persist->num_vfs = 0;
        kfree(dev->dev_vfs);
-        dev->dev_vfs = NULL;
+       dev->dev_vfs = NULL;
        return dev_flags & ~MLX4_FLAG_MASTER;
 }
 
index 1183248..8760d10 100644 (file)
@@ -49,6 +49,7 @@
 #include <net/switchdev.h>
 #include <net/xdp.h>
 #include <linux/net_dim.h>
+#include <linux/bits.h>
 #include "wq.h"
 #include "mlx5_core.h"
 #include "en_stats.h"
@@ -147,9 +148,6 @@ struct page_pool;
               MLX5_UMR_MTT_ALIGNMENT))
 #define MLX5E_UMR_WQEBBS \
        (DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_BB))
-#define MLX5E_ICOSQ_MAX_WQEBBS MLX5E_UMR_WQEBBS
-
-#define MLX5E_NUM_MAIN_GROUPS 9
 
 #define MLX5E_MSG_LEVEL                        NETIF_MSG_LINK
 
@@ -214,22 +212,23 @@ struct mlx5e_umr_wqe {
 extern const char mlx5e_self_tests[][ETH_GSTRING_LEN];
 
 enum mlx5e_priv_flag {
-       MLX5E_PFLAG_RX_CQE_BASED_MODER = (1 << 0),
-       MLX5E_PFLAG_TX_CQE_BASED_MODER = (1 << 1),
-       MLX5E_PFLAG_RX_CQE_COMPRESS = (1 << 2),
-       MLX5E_PFLAG_RX_STRIDING_RQ = (1 << 3),
-       MLX5E_PFLAG_RX_NO_CSUM_COMPLETE = (1 << 4),
+       MLX5E_PFLAG_RX_CQE_BASED_MODER,
+       MLX5E_PFLAG_TX_CQE_BASED_MODER,
+       MLX5E_PFLAG_RX_CQE_COMPRESS,
+       MLX5E_PFLAG_RX_STRIDING_RQ,
+       MLX5E_PFLAG_RX_NO_CSUM_COMPLETE,
+       MLX5E_NUM_PFLAGS, /* Keep last */
 };
 
 #define MLX5E_SET_PFLAG(params, pflag, enable)                 \
        do {                                                    \
                if (enable)                                     \
-                       (params)->pflags |= (pflag);            \
+                       (params)->pflags |= BIT(pflag);         \
                else                                            \
-                       (params)->pflags &= ~(pflag);           \
+                       (params)->pflags &= ~(BIT(pflag));      \
        } while (0)
 
-#define MLX5E_GET_PFLAG(params, pflag) (!!((params)->pflags & (pflag)))
+#define MLX5E_GET_PFLAG(params, pflag) (!!((params)->pflags & (BIT(pflag))))
 
 #ifdef CONFIG_MLX5_CORE_EN_DCB
 #define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */
@@ -247,9 +246,6 @@ struct mlx5e_params {
        bool lro_en;
        u32 lro_wqe_sz;
        u8  tx_min_inline_mode;
-       u8  rss_hfunc;
-       u8  toeplitz_hash_key[40];
-       u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE];
        bool vlan_strip_disable;
        bool scatter_fcs_en;
        bool rx_dim_enabled;
@@ -654,6 +650,13 @@ enum {
        MLX5E_NIC_PRIO
 };
 
+struct mlx5e_rss_params {
+       u32     indirection_rqt[MLX5E_INDIR_RQT_SIZE];
+       u32     rx_hash_fields[MLX5E_NUM_INDIR_TIRS];
+       u8      toeplitz_hash_key[40];
+       u8      hfunc;
+};
+
 struct mlx5e_priv {
        /* priv data path fields - start */
        struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC];
@@ -674,6 +677,7 @@ struct mlx5e_priv {
        struct mlx5e_tir           indir_tir[MLX5E_NUM_INDIR_TIRS];
        struct mlx5e_tir           inner_indir_tir[MLX5E_NUM_INDIR_TIRS];
        struct mlx5e_tir           direct_tir[MLX5E_MAX_NUM_CHANNELS];
+       struct mlx5e_rss_params    rss_params;
        u32                        tx_rates[MLX5E_MAX_NUM_SQS];
 
        struct mlx5e_flow_steering fs;
@@ -799,9 +803,11 @@ struct mlx5e_redirect_rqt_param {
 
 int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz,
                       struct mlx5e_redirect_rqt_param rrp);
-void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params,
-                                   enum mlx5e_traffic_types tt,
+void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_rss_params *rss_params,
+                                   const struct mlx5e_tirc_config *ttconfig,
                                    void *tirc, bool inner);
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen);
+struct mlx5e_tirc_config mlx5e_tirc_get_default_config(enum mlx5e_traffic_types tt);
 
 int mlx5e_open_locked(struct net_device *netdev);
 int mlx5e_close_locked(struct net_device *netdev);
@@ -983,11 +989,13 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv);
 void mlx5e_detach_netdev(struct mlx5e_priv *priv);
 void mlx5e_destroy_netdev(struct mlx5e_priv *priv);
 void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
+                           struct mlx5e_rss_params *rss_params,
                            struct mlx5e_params *params,
                            u16 max_channels, u16 mtu);
 void mlx5e_build_rq_params(struct mlx5_core_dev *mdev,
                           struct mlx5e_params *params);
-void mlx5e_build_rss_params(struct mlx5e_params *params);
+void mlx5e_build_rss_params(struct mlx5e_rss_params *rss_params,
+                           u16 num_channels);
 u8 mlx5e_params_calculate_tx_min_inline(struct mlx5_core_dev *mdev);
 void mlx5e_rx_dim_work(struct work_struct *work);
 void mlx5e_tx_dim_work(struct work_struct *work);
index 1431232..be5961f 100644 (file)
@@ -73,6 +73,22 @@ enum mlx5e_traffic_types {
        MLX5E_NUM_INDIR_TIRS = MLX5E_TT_ANY,
 };
 
+struct mlx5e_tirc_config {
+       u8 l3_prot_type;
+       u8 l4_prot_type;
+       u32 rx_hash_fields;
+};
+
+#define MLX5_HASH_IP           (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP)
+#define MLX5_HASH_IP_L4PORTS   (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP   |\
+                                MLX5_HASH_FIELD_SEL_L4_SPORT |\
+                                MLX5_HASH_FIELD_SEL_L4_DPORT)
+#define MLX5_HASH_IP_IPSEC_SPI (MLX5_HASH_FIELD_SEL_SRC_IP   |\
+                                MLX5_HASH_FIELD_SEL_DST_IP   |\
+                                MLX5_HASH_FIELD_SEL_IPSEC_SPI)
+
 enum mlx5e_tunnel_types {
        MLX5E_TT_IPV4_GRE,
        MLX5E_TT_IPV6_GRE,
index 25c1c4f..da4bb62 100644 (file)
@@ -135,14 +135,15 @@ void mlx5e_build_ptys2ethtool_map(void)
                                       ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT);
 }
 
-static const char mlx5e_priv_flags[][ETH_GSTRING_LEN] = {
-       "rx_cqe_moder",
-       "tx_cqe_moder",
-       "rx_cqe_compress",
-       "rx_striding_rq",
-       "rx_no_csum_complete",
+typedef int (*mlx5e_pflag_handler)(struct net_device *netdev, bool enable);
+
+struct pflag_desc {
+       char name[ETH_GSTRING_LEN];
+       mlx5e_pflag_handler handler;
 };
 
+static const struct pflag_desc mlx5e_priv_flags[MLX5E_NUM_PFLAGS];
+
 int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset)
 {
        int i, num_stats = 0;
@@ -153,7 +154,7 @@ int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset)
                        num_stats += mlx5e_stats_grps[i].get_num_stats(priv);
                return num_stats;
        case ETH_SS_PRIV_FLAGS:
-               return ARRAY_SIZE(mlx5e_priv_flags);
+               return MLX5E_NUM_PFLAGS;
        case ETH_SS_TEST:
                return mlx5e_self_test_num(priv);
        /* fallthrough */
@@ -183,8 +184,9 @@ void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, u32 stringset, u8 *data)
 
        switch (stringset) {
        case ETH_SS_PRIV_FLAGS:
-               for (i = 0; i < ARRAY_SIZE(mlx5e_priv_flags); i++)
-                       strcpy(data + i * ETH_GSTRING_LEN, mlx5e_priv_flags[i]);
+               for (i = 0; i < MLX5E_NUM_PFLAGS; i++)
+                       strcpy(data + i * ETH_GSTRING_LEN,
+                              mlx5e_priv_flags[i].name);
                break;
 
        case ETH_SS_TEST:
@@ -353,7 +355,7 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
        new_channels.params = priv->channels.params;
        new_channels.params.num_channels = count;
        if (!netif_is_rxfh_configured(priv->netdev))
-               mlx5e_build_default_indir_rqt(new_channels.params.indirection_rqt,
+               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
                                              MLX5E_INDIR_RQT_SIZE, count);
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
@@ -931,7 +933,7 @@ out:
 
 u32 mlx5e_ethtool_get_rxfh_key_size(struct mlx5e_priv *priv)
 {
-       return sizeof(priv->channels.params.toeplitz_hash_key);
+       return sizeof(priv->rss_params.toeplitz_hash_key);
 }
 
 static u32 mlx5e_get_rxfh_key_size(struct net_device *netdev)
@@ -957,50 +959,27 @@ static int mlx5e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
                          u8 *hfunc)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
 
        if (indir)
-               memcpy(indir, priv->channels.params.indirection_rqt,
-                      sizeof(priv->channels.params.indirection_rqt));
+               memcpy(indir, rss->indirection_rqt,
+                      sizeof(rss->indirection_rqt));
 
        if (key)
-               memcpy(key, priv->channels.params.toeplitz_hash_key,
-                      sizeof(priv->channels.params.toeplitz_hash_key));
+               memcpy(key, rss->toeplitz_hash_key,
+                      sizeof(rss->toeplitz_hash_key));
 
        if (hfunc)
-               *hfunc = priv->channels.params.rss_hfunc;
+               *hfunc = rss->hfunc;
 
        return 0;
 }
 
-static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
-{
-       void *tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx);
-       struct mlx5_core_dev *mdev = priv->mdev;
-       int ctxlen = MLX5_ST_SZ_BYTES(tirc);
-       int tt;
-
-       MLX5_SET(modify_tir_in, in, bitmask.hash, 1);
-
-       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
-               memset(tirc, 0, ctxlen);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
-               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen);
-       }
-
-       if (!mlx5e_tunnel_inner_ft_supported(priv->mdev))
-               return;
-
-       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
-               memset(tirc, 0, ctxlen);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true);
-               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in, inlen);
-       }
-}
-
 static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
                          const u8 *key, const u8 hfunc)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
        int inlen = MLX5_ST_SZ_BYTES(modify_tir_in);
        bool hash_changed = false;
        void *in;
@@ -1016,15 +995,14 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
 
        mutex_lock(&priv->state_lock);
 
-       if (hfunc != ETH_RSS_HASH_NO_CHANGE &&
-           hfunc != priv->channels.params.rss_hfunc) {
-               priv->channels.params.rss_hfunc = hfunc;
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != rss->hfunc) {
+               rss->hfunc = hfunc;
                hash_changed = true;
        }
 
        if (indir) {
-               memcpy(priv->channels.params.indirection_rqt, indir,
-                      sizeof(priv->channels.params.indirection_rqt));
+               memcpy(rss->indirection_rqt, indir,
+                      sizeof(rss->indirection_rqt));
 
                if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
                        u32 rqtn = priv->indir_rqt.rqtn;
@@ -1032,7 +1010,7 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
                                .is_rss = true,
                                {
                                        .rss = {
-                                               .hfunc = priv->channels.params.rss_hfunc,
+                                               .hfunc = rss->hfunc,
                                                .channels  = &priv->channels,
                                        },
                                },
@@ -1043,10 +1021,9 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
        }
 
        if (key) {
-               memcpy(priv->channels.params.toeplitz_hash_key, key,
-                      sizeof(priv->channels.params.toeplitz_hash_key));
-               hash_changed = hash_changed ||
-                              priv->channels.params.rss_hfunc == ETH_RSS_HASH_TOP;
+               memcpy(rss->toeplitz_hash_key, key,
+                      sizeof(rss->toeplitz_hash_key));
+               hash_changed = hash_changed || rss->hfunc == ETH_RSS_HASH_TOP;
        }
 
        if (hash_changed)
@@ -1510,8 +1487,6 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev,
        return 0;
 }
 
-typedef int (*mlx5e_pflag_handler)(struct net_device *netdev, bool enable);
-
 static int set_pflag_cqe_based_moder(struct net_device *netdev, bool enable,
                                     bool is_rx_cq)
 {
@@ -1674,23 +1649,30 @@ static int set_pflag_rx_no_csum_complete(struct net_device *netdev, bool enable)
        return 0;
 }
 
+static const struct pflag_desc mlx5e_priv_flags[MLX5E_NUM_PFLAGS] = {
+       { "rx_cqe_moder",        set_pflag_rx_cqe_based_moder },
+       { "tx_cqe_moder",        set_pflag_tx_cqe_based_moder },
+       { "rx_cqe_compress",     set_pflag_rx_cqe_compress },
+       { "rx_striding_rq",      set_pflag_rx_striding_rq },
+       { "rx_no_csum_complete", set_pflag_rx_no_csum_complete },
+};
+
 static int mlx5e_handle_pflag(struct net_device *netdev,
                              u32 wanted_flags,
-                             enum mlx5e_priv_flag flag,
-                             mlx5e_pflag_handler pflag_handler)
+                             enum mlx5e_priv_flag flag)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
-       bool enable = !!(wanted_flags & flag);
+       bool enable = !!(wanted_flags & BIT(flag));
        u32 changes = wanted_flags ^ priv->channels.params.pflags;
        int err;
 
-       if (!(changes & flag))
+       if (!(changes & BIT(flag)))
                return 0;
 
-       err = pflag_handler(netdev, enable);
+       err = mlx5e_priv_flags[flag].handler(netdev, enable);
        if (err) {
-               netdev_err(netdev, "%s private flag 0x%x failed err %d\n",
-                          enable ? "Enable" : "Disable", flag, err);
+               netdev_err(netdev, "%s private flag '%s' failed err %d\n",
+                          enable ? "Enable" : "Disable", mlx5e_priv_flags[flag].name, err);
                return err;
        }
 
@@ -1701,38 +1683,17 @@ static int mlx5e_handle_pflag(struct net_device *netdev,
 static int mlx5e_set_priv_flags(struct net_device *netdev, u32 pflags)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       enum mlx5e_priv_flag pflag;
        int err;
 
        mutex_lock(&priv->state_lock);
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_CQE_BASED_MODER,
-                                set_pflag_rx_cqe_based_moder);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_TX_CQE_BASED_MODER,
-                                set_pflag_tx_cqe_based_moder);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_CQE_COMPRESS,
-                                set_pflag_rx_cqe_compress);
-       if (err)
-               goto out;
-
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_STRIDING_RQ,
-                                set_pflag_rx_striding_rq);
-       if (err)
-               goto out;
 
-       err = mlx5e_handle_pflag(netdev, pflags,
-                                MLX5E_PFLAG_RX_NO_CSUM_COMPLETE,
-                                set_pflag_rx_no_csum_complete);
+       for (pflag = 0; pflag < MLX5E_NUM_PFLAGS; pflag++) {
+               err = mlx5e_handle_pflag(netdev, pflags, pflag);
+               if (err)
+                       break;
+       }
 
-out:
        mutex_unlock(&priv->state_lock);
 
        /* Need to fix some features.. */
index c18dceb..4421c10 100644 (file)
@@ -771,6 +771,112 @@ void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv)
        INIT_LIST_HEAD(&priv->fs.ethtool.rules);
 }
 
+static enum mlx5e_traffic_types flow_type_to_traffic_type(u32 flow_type)
+{
+       switch (flow_type) {
+       case TCP_V4_FLOW:
+               return  MLX5E_TT_IPV4_TCP;
+       case TCP_V6_FLOW:
+               return MLX5E_TT_IPV6_TCP;
+       case UDP_V4_FLOW:
+               return MLX5E_TT_IPV4_UDP;
+       case UDP_V6_FLOW:
+               return MLX5E_TT_IPV6_UDP;
+       case AH_V4_FLOW:
+               return MLX5E_TT_IPV4_IPSEC_AH;
+       case AH_V6_FLOW:
+               return MLX5E_TT_IPV6_IPSEC_AH;
+       case ESP_V4_FLOW:
+               return MLX5E_TT_IPV4_IPSEC_ESP;
+       case ESP_V6_FLOW:
+               return MLX5E_TT_IPV6_IPSEC_ESP;
+       case IPV4_FLOW:
+               return MLX5E_TT_IPV4;
+       case IPV6_FLOW:
+               return MLX5E_TT_IPV6;
+       default:
+               return MLX5E_NUM_INDIR_TIRS;
+       }
+}
+
+static int mlx5e_set_rss_hash_opt(struct mlx5e_priv *priv,
+                                 struct ethtool_rxnfc *nfc)
+{
+       int inlen = MLX5_ST_SZ_BYTES(modify_tir_in);
+       enum mlx5e_traffic_types tt;
+       u8 rx_hash_field = 0;
+       void *in;
+
+       tt = flow_type_to_traffic_type(nfc->flow_type);
+       if (tt == MLX5E_NUM_INDIR_TIRS)
+               return -EINVAL;
+
+       /*  RSS does not support anything other than hashing to queues
+        *  on src IP, dest IP, TCP/UDP src port and TCP/UDP dest
+        *  port.
+        */
+       if (nfc->flow_type != TCP_V4_FLOW &&
+           nfc->flow_type != TCP_V6_FLOW &&
+           nfc->flow_type != UDP_V4_FLOW &&
+           nfc->flow_type != UDP_V6_FLOW)
+               return -EOPNOTSUPP;
+
+       if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+                         RXH_L4_B_0_1 | RXH_L4_B_2_3))
+               return -EOPNOTSUPP;
+
+       if (nfc->data & RXH_IP_SRC)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_SRC_IP;
+       if (nfc->data & RXH_IP_DST)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_DST_IP;
+       if (nfc->data & RXH_L4_B_0_1)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_L4_SPORT;
+       if (nfc->data & RXH_L4_B_2_3)
+               rx_hash_field |= MLX5_HASH_FIELD_SEL_L4_DPORT;
+
+       in = kvzalloc(inlen, GFP_KERNEL);
+       if (!in)
+               return -ENOMEM;
+
+       mutex_lock(&priv->state_lock);
+
+       if (rx_hash_field == priv->rss_params.rx_hash_fields[tt])
+               goto out;
+
+       priv->rss_params.rx_hash_fields[tt] = rx_hash_field;
+       mlx5e_modify_tirs_hash(priv, in, inlen);
+
+out:
+       mutex_unlock(&priv->state_lock);
+       kvfree(in);
+       return 0;
+}
+
+static int mlx5e_get_rss_hash_opt(struct mlx5e_priv *priv,
+                                 struct ethtool_rxnfc *nfc)
+{
+       enum mlx5e_traffic_types tt;
+       u32 hash_field = 0;
+
+       tt = flow_type_to_traffic_type(nfc->flow_type);
+       if (tt == MLX5E_NUM_INDIR_TIRS)
+               return -EINVAL;
+
+       hash_field = priv->rss_params.rx_hash_fields[tt];
+       nfc->data = 0;
+
+       if (hash_field & MLX5_HASH_FIELD_SEL_SRC_IP)
+               nfc->data |= RXH_IP_SRC;
+       if (hash_field & MLX5_HASH_FIELD_SEL_DST_IP)
+               nfc->data |= RXH_IP_DST;
+       if (hash_field & MLX5_HASH_FIELD_SEL_L4_SPORT)
+               nfc->data |= RXH_L4_B_0_1;
+       if (hash_field & MLX5_HASH_FIELD_SEL_L4_DPORT)
+               nfc->data |= RXH_L4_B_2_3;
+
+       return 0;
+}
+
 int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
 {
        int err = 0;
@@ -783,6 +889,9 @@ int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
        case ETHTOOL_SRXCLSRLDEL:
                err = mlx5e_ethtool_flow_remove(priv, cmd->fs.location);
                break;
+       case ETHTOOL_SRXFH:
+               err = mlx5e_set_rss_hash_opt(priv, cmd);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -810,6 +919,9 @@ int mlx5e_get_rxnfc(struct net_device *dev,
        case ETHTOOL_GRXCLSRLALL:
                err = mlx5e_ethtool_get_all_flows(priv, info, rule_locs);
                break;
+       case ETHTOOL_GRXFH:
+               err =  mlx5e_get_rss_hash_opt(priv, info);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
index 871313d..544c287 100644 (file)
@@ -2504,7 +2504,7 @@ static void mlx5e_fill_rqt_rqns(struct mlx5e_priv *priv, int sz,
                        if (rrp.rss.hfunc == ETH_RSS_HASH_XOR)
                                ix = mlx5e_bits_invert(i, ilog2(sz));
 
-                       ix = priv->channels.params.indirection_rqt[ix];
+                       ix = priv->rss_params.indirection_rqt[ix];
                        rqn = rrp.rss.channels->c[ix]->rq.rqn;
                } else {
                        rqn = rrp.rqn;
@@ -2587,7 +2587,7 @@ static void mlx5e_redirect_rqts_to_channels(struct mlx5e_priv *priv,
                {
                        .rss = {
                                .channels  = chs,
-                               .hfunc     = chs->params.rss_hfunc,
+                               .hfunc     = priv->rss_params.hfunc,
                        }
                },
        };
@@ -2607,6 +2607,54 @@ static void mlx5e_redirect_rqts_to_drop(struct mlx5e_priv *priv)
        mlx5e_redirect_rqts(priv, drop_rrp);
 }
 
+static const struct mlx5e_tirc_config tirc_default_config[MLX5E_NUM_INDIR_TIRS] = {
+       [MLX5E_TT_IPV4_TCP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV6_TCP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV4_UDP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV6_UDP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                               .l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
+                               .rx_hash_fields = MLX5_HASH_IP_L4PORTS,
+       },
+       [MLX5E_TT_IPV4_IPSEC_AH] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                                    .l4_prot_type = 0,
+                                    .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV6_IPSEC_AH] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                                    .l4_prot_type = 0,
+                                    .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV4_IPSEC_ESP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                                     .l4_prot_type = 0,
+                                     .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV6_IPSEC_ESP] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                                     .l4_prot_type = 0,
+                                     .rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
+       },
+       [MLX5E_TT_IPV4] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
+                           .l4_prot_type = 0,
+                           .rx_hash_fields = MLX5_HASH_IP,
+       },
+       [MLX5E_TT_IPV6] = { .l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
+                           .l4_prot_type = 0,
+                           .rx_hash_fields = MLX5_HASH_IP,
+       },
+};
+
+struct mlx5e_tirc_config mlx5e_tirc_get_default_config(enum mlx5e_traffic_types tt)
+{
+       return tirc_default_config[tt];
+}
+
 static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc)
 {
        if (!params->lro_en)
@@ -2622,116 +2670,68 @@ static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc)
        MLX5_SET(tirc, tirc, lro_timeout_period_usecs, params->lro_timeout);
 }
 
-void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params,
-                                   enum mlx5e_traffic_types tt,
+void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_rss_params *rss_params,
+                                   const struct mlx5e_tirc_config *ttconfig,
                                    void *tirc, bool inner)
 {
        void *hfso = inner ? MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner) :
                             MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);
 
-#define MLX5_HASH_IP            (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP)
-
-#define MLX5_HASH_IP_L4PORTS    (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP   |\
-                                MLX5_HASH_FIELD_SEL_L4_SPORT |\
-                                MLX5_HASH_FIELD_SEL_L4_DPORT)
-
-#define MLX5_HASH_IP_IPSEC_SPI  (MLX5_HASH_FIELD_SEL_SRC_IP   |\
-                                MLX5_HASH_FIELD_SEL_DST_IP   |\
-                                MLX5_HASH_FIELD_SEL_IPSEC_SPI)
-
-       MLX5_SET(tirc, tirc, rx_hash_fn, mlx5e_rx_hash_fn(params->rss_hfunc));
-       if (params->rss_hfunc == ETH_RSS_HASH_TOP) {
+       MLX5_SET(tirc, tirc, rx_hash_fn, mlx5e_rx_hash_fn(rss_params->hfunc));
+       if (rss_params->hfunc == ETH_RSS_HASH_TOP) {
                void *rss_key = MLX5_ADDR_OF(tirc, tirc,
                                             rx_hash_toeplitz_key);
                size_t len = MLX5_FLD_SZ_BYTES(tirc,
                                               rx_hash_toeplitz_key);
 
                MLX5_SET(tirc, tirc, rx_hash_symmetric, 1);
-               memcpy(rss_key, params->toeplitz_hash_key, len);
+               memcpy(rss_key, rss_params->toeplitz_hash_key, len);
        }
+       MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
+                ttconfig->l3_prot_type);
+       MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
+                ttconfig->l4_prot_type);
+       MLX5_SET(rx_hash_field_select, hfso, selected_fields,
+                ttconfig->rx_hash_fields);
+}
 
-       switch (tt) {
-       case MLX5E_TT_IPV4_TCP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_TCP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV6_TCP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_TCP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV4_UDP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_UDP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV6_UDP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, l4_prot_type,
-                        MLX5_L4_PROT_TYPE_UDP);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_L4PORTS);
-               break;
-
-       case MLX5E_TT_IPV4_IPSEC_AH:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+static void mlx5e_update_rx_hash_fields(struct mlx5e_tirc_config *ttconfig,
+                                       enum mlx5e_traffic_types tt,
+                                       u32 rx_hash_fields)
+{
+       *ttconfig                = tirc_default_config[tt];
+       ttconfig->rx_hash_fields = rx_hash_fields;
+}
 
-       case MLX5E_TT_IPV6_IPSEC_AH:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
+{
+       void *tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
+       struct mlx5_core_dev *mdev = priv->mdev;
+       int ctxlen = MLX5_ST_SZ_BYTES(tirc);
+       struct mlx5e_tirc_config ttconfig;
+       int tt;
 
-       case MLX5E_TT_IPV4_IPSEC_ESP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+       MLX5_SET(modify_tir_in, in, bitmask.hash, 1);
 
-       case MLX5E_TT_IPV6_IPSEC_ESP:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP_IPSEC_SPI);
-               break;
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               memset(tirc, 0, ctxlen);
+               mlx5e_update_rx_hash_fields(&ttconfig, tt,
+                                           rss->rx_hash_fields[tt]);
+               mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, false);
+               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen);
+       }
 
-       case MLX5E_TT_IPV4:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV4);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP);
-               break;
+       if (!mlx5e_tunnel_inner_ft_supported(priv->mdev))
+               return;
 
-       case MLX5E_TT_IPV6:
-               MLX5_SET(rx_hash_field_select, hfso, l3_prot_type,
-                        MLX5_L3_PROT_TYPE_IPV6);
-               MLX5_SET(rx_hash_field_select, hfso, selected_fields,
-                        MLX5_HASH_IP);
-               break;
-       default:
-               WARN_ONCE(true, "%s: bad traffic type!\n", __func__);
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               memset(tirc, 0, ctxlen);
+               mlx5e_update_rx_hash_fields(&ttconfig, tt,
+                                           rss->rx_hash_fields[tt]);
+               mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, true);
+               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in,
+                                    inlen);
        }
 }
 
@@ -2788,7 +2788,8 @@ static void mlx5e_build_inner_indir_tir_ctx(struct mlx5e_priv *priv,
        MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn);
        MLX5_SET(tirc, tirc, tunneled_offload_en, 0x1);
 
-       mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true);
+       mlx5e_build_indir_tir_ctx_hash(&priv->rss_params,
+                                      &tirc_default_config[tt], tirc, true);
 }
 
 static int mlx5e_set_mtu(struct mlx5_core_dev *mdev,
@@ -3180,7 +3181,9 @@ static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv,
 
        MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
        MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn);
-       mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
+
+       mlx5e_build_indir_tir_ctx_hash(&priv->rss_params,
+                                      &tirc_default_config[tt], tirc, false);
 }
 
 static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *tirc)
@@ -4518,15 +4521,23 @@ void mlx5e_build_rq_params(struct mlx5_core_dev *mdev,
        mlx5e_init_rq_type_params(mdev, params);
 }
 
-void mlx5e_build_rss_params(struct mlx5e_params *params)
+void mlx5e_build_rss_params(struct mlx5e_rss_params *rss_params,
+                           u16 num_channels)
 {
-       params->rss_hfunc = ETH_RSS_HASH_XOR;
-       netdev_rss_key_fill(params->toeplitz_hash_key, sizeof(params->toeplitz_hash_key));
-       mlx5e_build_default_indir_rqt(params->indirection_rqt,
-                                     MLX5E_INDIR_RQT_SIZE, params->num_channels);
+       enum mlx5e_traffic_types tt;
+
+       rss_params->hfunc = ETH_RSS_HASH_XOR;
+       netdev_rss_key_fill(rss_params->toeplitz_hash_key,
+                           sizeof(rss_params->toeplitz_hash_key));
+       mlx5e_build_default_indir_rqt(rss_params->indirection_rqt,
+                                     MLX5E_INDIR_RQT_SIZE, num_channels);
+       for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
+               rss_params->rx_hash_fields[tt] =
+                       tirc_default_config[tt].rx_hash_fields;
 }
 
 void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
+                           struct mlx5e_rss_params *rss_params,
                            struct mlx5e_params *params,
                            u16 max_channels, u16 mtu)
 {
@@ -4575,7 +4586,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
        params->tx_min_inline_mode = mlx5e_params_calculate_tx_min_inline(mdev);
 
        /* RSS */
-       mlx5e_build_rss_params(params);
+       mlx5e_build_rss_params(rss_params, params->num_channels);
 }
 
 static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
@@ -4748,14 +4759,16 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
                          void *ppriv)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5e_rss_params *rss = &priv->rss_params;
        int err;
 
        err = mlx5e_netdev_init(netdev, priv, mdev, profile, ppriv);
        if (err)
                return err;
 
-       mlx5e_build_nic_params(mdev, &priv->channels.params,
-                              mlx5e_get_netdev_max_channels(netdev), netdev->mtu);
+       mlx5e_build_nic_params(mdev, rss, &priv->channels.params,
+                              mlx5e_get_netdev_max_channels(netdev),
+                              netdev->mtu);
 
        mlx5e_timestamp_init(priv);
 
@@ -5030,7 +5043,7 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
        if (priv->channels.params.num_channels > max_nch) {
                mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n", max_nch);
                priv->channels.params.num_channels = max_nch;
-               mlx5e_build_default_indir_rqt(priv->channels.params.indirection_rqt,
+               mlx5e_build_default_indir_rqt(priv->rss_params.indirection_rqt,
                                              MLX5E_INDIR_RQT_SIZE, max_nch);
        }
 
index c3c6575..a137540 100644 (file)
@@ -1012,7 +1012,9 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = {
 };
 
 static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev,
-                                  struct mlx5e_params *params, u16 mtu)
+                                  struct mlx5e_params *params,
+                                  struct mlx5e_rss_params *rss_params,
+                                  u16 mtu)
 {
        u8 cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ?
                                         MLX5_CQ_PERIOD_MODE_START_FROM_CQE :
@@ -1034,7 +1036,7 @@ static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev,
        mlx5_query_min_inline(mdev, &params->tx_min_inline_mode);
 
        /* RSS */
-       mlx5e_build_rss_params(params);
+       mlx5e_build_rss_params(rss_params, params->num_channels);
 }
 
 static void mlx5e_build_rep_netdev(struct net_device *netdev)
@@ -1087,7 +1089,8 @@ static int mlx5e_init_rep(struct mlx5_core_dev *mdev,
        priv->channels.params.num_channels =
                                mlx5e_get_netdev_max_channels(netdev);
 
-       mlx5e_build_rep_params(mdev, &priv->channels.params, netdev->mtu);
+       mlx5e_build_rep_params(mdev, &priv->channels.params,
+                              &priv->rss_params, netdev->mtu);
        mlx5e_build_rep_netdev(netdev);
 
        mlx5e_timestamp_init(priv);
index 3e99d07..40b60e9 100644 (file)
@@ -936,7 +936,7 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = {
 };
 
 static const struct counter_desc pport_pfc_stall_stats_desc[] = {
-       { "tx_pause_storm_warning_events ", PPORT_PER_PRIO_OFF(device_stall_minor_watermark_cnt) },
+       { "tx_pause_storm_warning_events", PPORT_PER_PRIO_OFF(device_stall_minor_watermark_cnt) },
        { "tx_pause_storm_error_events", PPORT_PER_PRIO_OFF(device_stall_critical_watermark_cnt) },
 };
 
index fca6f41..a3ff249 100644 (file)
@@ -316,7 +316,7 @@ static void mlx5e_hairpin_fill_rqt_rqns(struct mlx5e_hairpin *hp, void *rqtc)
 
        for (i = 0; i < sz; i++) {
                ix = i;
-               if (priv->channels.params.rss_hfunc == ETH_RSS_HASH_XOR)
+               if (priv->rss_params.hfunc == ETH_RSS_HASH_XOR)
                        ix = mlx5e_bits_invert(i, ilog2(sz));
                ix = indirection_rqt[ix];
                rqn = hp->pair->rqn[ix];
@@ -360,13 +360,15 @@ static int mlx5e_hairpin_create_indirect_tirs(struct mlx5e_hairpin *hp)
        void *tirc;
 
        for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
+               struct mlx5e_tirc_config ttconfig = mlx5e_tirc_get_default_config(tt);
+
                memset(in, 0, MLX5_ST_SZ_BYTES(create_tir_in));
                tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
 
                MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
                MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
                MLX5_SET(tirc, tirc, indirect_table, hp->indir_rqt.rqtn);
-               mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false);
+               mlx5e_build_indir_tir_ctx_hash(&priv->rss_params, &ttconfig, tirc, false);
 
                err = mlx5_core_create_tir(hp->func_mdev, in,
                                           MLX5_ST_SZ_BYTES(create_tir_in), &hp->indir_tirn[tt]);
index 6dacaeb..9afdf95 100644 (file)
@@ -127,7 +127,7 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb,
        else
 #endif
                if (skb_vlan_tag_present(skb))
-                       up = skb->vlan_tci >> VLAN_PRIO_SHIFT;
+                       up = skb_vlan_tag_get_prio(skb);
 
        /* channel_ix can be larger than num_channels since
         * dev->num_real_tx_queues = num_channels * num_tc
index 11dabd6..bfc0f65 100644 (file)
@@ -87,7 +87,7 @@ int mlx5i_init(struct mlx5_core_dev *mdev,
        mlx5_query_port_max_mtu(mdev, &max_mtu, 1);
        netdev->mtu = max_mtu;
 
-       mlx5e_build_nic_params(mdev, &priv->channels.params,
+       mlx5e_build_nic_params(mdev, &priv->rss_params, &priv->channels.params,
                               mlx5e_get_netdev_max_channels(netdev),
                               netdev->mtu);
        mlx5i_build_nic_params(mdev, &priv->channels.params);
index 8a291eb..080ddd1 100644 (file)
@@ -80,6 +80,7 @@ config MLXSW_SPECTRUM
        depends on IPV6_GRE || IPV6_GRE=n
        select GENERIC_ALLOCATOR
        select PARMAN
+       select OBJAGG
        select MLXFW
        default m
        ---help---
index 30f751e..281aeb1 100644 (file)
@@ -965,10 +965,11 @@ static const struct devlink_ops mlxsw_devlink_ops = {
        .sb_occ_tc_port_bind_get        = mlxsw_devlink_sb_occ_tc_port_bind_get,
 };
 
-int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
-                                  const struct mlxsw_bus *mlxsw_bus,
-                                  void *bus_priv, bool reload,
-                                  struct devlink *devlink)
+static int
+__mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
+                                const struct mlxsw_bus *mlxsw_bus,
+                                void *bus_priv, bool reload,
+                                struct devlink *devlink)
 {
        const char *device_kind = mlxsw_bus_info->device_kind;
        struct mlxsw_core *mlxsw_core;
@@ -1035,6 +1036,12 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
                        goto err_devlink_register;
        }
 
+       if (mlxsw_driver->params_register && !reload) {
+               err = mlxsw_driver->params_register(mlxsw_core);
+               if (err)
+                       goto err_register_params;
+       }
+
        err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
        if (err)
                goto err_hwmon_init;
@@ -1057,6 +1064,9 @@ err_driver_init:
 err_thermal_init:
        mlxsw_hwmon_fini(mlxsw_core->hwmon);
 err_hwmon_init:
+       if (mlxsw_driver->params_unregister && !reload)
+               mlxsw_driver->params_unregister(mlxsw_core);
+err_register_params:
        if (!reload)
                devlink_unregister(devlink);
 err_devlink_register:
@@ -1076,6 +1086,29 @@ err_bus_init:
 err_devlink_alloc:
        return err;
 }
+
+int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
+                                  const struct mlxsw_bus *mlxsw_bus,
+                                  void *bus_priv, bool reload,
+                                  struct devlink *devlink)
+{
+       bool called_again = false;
+       int err;
+
+again:
+       err = __mlxsw_core_bus_device_register(mlxsw_bus_info, mlxsw_bus,
+                                              bus_priv, reload, devlink);
+       /* -EAGAIN is returned in case the FW was updated. FW needs
+        * a reset, so lets try to call __mlxsw_core_bus_device_register()
+        * again.
+        */
+       if (err == -EAGAIN && !called_again) {
+               called_again = true;
+               goto again;
+       }
+
+       return err;
+}
 EXPORT_SYMBOL(mlxsw_core_bus_device_register);
 
 void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
@@ -1097,6 +1130,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
                mlxsw_core->driver->fini(mlxsw_core);
        mlxsw_thermal_fini(mlxsw_core->thermal);
        mlxsw_hwmon_fini(mlxsw_core->hwmon);
+       if (mlxsw_core->driver->params_unregister && !reload)
+               mlxsw_core->driver->params_unregister(mlxsw_core);
        if (!reload)
                devlink_unregister(devlink);
        mlxsw_emad_fini(mlxsw_core);
@@ -1109,6 +1144,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
        return;
 
 reload_fail_deinit:
+       if (mlxsw_core->driver->params_unregister)
+               mlxsw_core->driver->params_unregister(mlxsw_core);
        devlink_unregister(devlink);
        devlink_resources_unregister(devlink, NULL);
        devlink_free(devlink);
index c35be47..d811be8 100644 (file)
@@ -282,6 +282,8 @@ struct mlxsw_driver {
                             const struct mlxsw_config_profile *profile,
                             u64 *p_single_size, u64 *p_double_size,
                             u64 *p_linear_size);
+       int (*params_register)(struct mlxsw_core *mlxsw_core);
+       void (*params_unregister)(struct mlxsw_core *mlxsw_core);
        u8 txhdr_len;
        const struct mlxsw_config_profile *profile;
        bool res_query_enabled;
index 785bf01..df78d23 100644 (file)
@@ -426,15 +426,17 @@ mlxsw_sp_afk_encode_one(const struct mlxsw_afk_element_inst *elinst,
 void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                      struct mlxsw_afk_key_info *key_info,
                      struct mlxsw_afk_element_values *values,
-                     char *key, char *mask, int block_start, int block_end)
+                     char *key, char *mask)
 {
+       unsigned int blocks_count =
+                       mlxsw_afk_key_info_blocks_count_get(key_info);
        char block_mask[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
        char block_key[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
        const struct mlxsw_afk_element_inst *elinst;
        enum mlxsw_afk_element element;
        int block_index, i;
 
-       for (i = block_start; i <= block_end; i++) {
+       for (i = 0; i < blocks_count; i++) {
                memset(block_key, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
                memset(block_mask, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
 
@@ -451,10 +453,18 @@ void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                                                values->storage.mask);
                }
 
-               if (key)
-                       mlxsw_afk->ops->encode_block(block_key, i, key);
-               if (mask)
-                       mlxsw_afk->ops->encode_block(block_mask, i, mask);
+               mlxsw_afk->ops->encode_block(key, i, block_key);
+               mlxsw_afk->ops->encode_block(mask, i, block_mask);
        }
 }
 EXPORT_SYMBOL(mlxsw_afk_encode);
+
+void mlxsw_afk_clear(struct mlxsw_afk *mlxsw_afk, char *key,
+                    int block_start, int block_end)
+{
+       int i;
+
+       for (i = block_start; i <= block_end; i++)
+               mlxsw_afk->ops->clear_block(key, i);
+}
+EXPORT_SYMBOL(mlxsw_afk_clear);
index c29c045..bcd2641 100644 (file)
@@ -188,7 +188,8 @@ struct mlxsw_afk;
 struct mlxsw_afk_ops {
        const struct mlxsw_afk_block *blocks;
        unsigned int blocks_count;
-       void (*encode_block)(char *block, int block_index, char *output);
+       void (*encode_block)(char *output, int block_index, char *block);
+       void (*clear_block)(char *output, int block_index);
 };
 
 struct mlxsw_afk *mlxsw_afk_create(unsigned int max_blocks,
@@ -228,6 +229,8 @@ void mlxsw_afk_values_add_buf(struct mlxsw_afk_element_values *values,
 void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
                      struct mlxsw_afk_key_info *key_info,
                      struct mlxsw_afk_element_values *values,
-                     char *key, char *mask, int block_start, int block_end);
+                     char *key, char *mask);
+void mlxsw_afk_clear(struct mlxsw_afk *mlxsw_afk, char *key,
+                    int block_start, int block_end);
 
 #endif
index 6d29dc4..61f897b 100644 (file)
 #define MLXSW_THERMAL_MAX_TEMP 110000  /* 110C */
 #define MLXSW_THERMAL_MAX_STATE        10
 #define MLXSW_THERMAL_MAX_DUTY 255
+/* Minimum and maximum fan allowed speed in percent: from 20% to 100%. Values
+ * MLXSW_THERMAL_MAX_STATE + x, where x is between 2 and 10 are used for
+ * setting fan speed dynamic minimum. For example, if value is set to 14 (40%)
+ * cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to
+ * introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100.
+ */
+#define MLXSW_THERMAL_SPEED_MIN                (MLXSW_THERMAL_MAX_STATE + 2)
+#define MLXSW_THERMAL_SPEED_MAX                (MLXSW_THERMAL_MAX_STATE * 2)
+#define MLXSW_THERMAL_SPEED_MIN_LEVEL  2               /* 20% */
 
 struct mlxsw_thermal_trip {
        int     type;
@@ -68,6 +77,7 @@ struct mlxsw_thermal {
        const struct mlxsw_bus_info *bus_info;
        struct thermal_zone_device *tzdev;
        struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
+       u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
        struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
        enum thermal_device_mode mode;
 };
@@ -285,12 +295,51 @@ static int mlxsw_thermal_set_cur_state(struct thermal_cooling_device *cdev,
        struct mlxsw_thermal *thermal = cdev->devdata;
        struct device *dev = thermal->bus_info->dev;
        char mfsc_pl[MLXSW_REG_MFSC_LEN];
-       int err, idx;
+       unsigned long cur_state, i;
+       int idx;
+       u8 duty;
+       int err;
 
        idx = mlxsw_get_cooling_device_idx(thermal, cdev);
        if (idx < 0)
                return idx;
 
+       /* Verify if this request is for changing allowed fan dynamical
+        * minimum. If it is - update cooling levels accordingly and update
+        * state, if current state is below the newly requested minimum state.
+        * For example, if current state is 5, and minimal state is to be
+        * changed from 4 to 6, thermal->cooling_levels[0 to 5] will be changed
+        * all from 4 to 6. And state 5 (thermal->cooling_levels[4]) should be
+        * overwritten.
+        */
+       if (state >= MLXSW_THERMAL_SPEED_MIN &&
+           state <= MLXSW_THERMAL_SPEED_MAX) {
+               state -= MLXSW_THERMAL_MAX_STATE;
+               for (i = 0; i <= MLXSW_THERMAL_MAX_STATE; i++)
+                       thermal->cooling_levels[i] = max(state, i);
+
+               mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0);
+               err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
+               if (err)
+                       return err;
+
+               duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl);
+               cur_state = mlxsw_duty_to_state(duty);
+
+               /* If current fan state is lower than requested dynamical
+                * minimum, increase fan speed up to dynamical minimum.
+                */
+               if (state < cur_state)
+                       return 0;
+
+               state = cur_state;
+       }
+
+       if (state > MLXSW_THERMAL_MAX_STATE)
+               return -EINVAL;
+
+       /* Normalize the state to the valid speed range. */
+       state = thermal->cooling_levels[state];
        mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state));
        err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
        if (err) {
@@ -369,6 +418,11 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
                }
        }
 
+       /* Initialize cooling levels per PWM state. */
+       for (i = 0; i < MLXSW_THERMAL_MAX_STATE; i++)
+               thermal->cooling_levels[i] = max(MLXSW_THERMAL_SPEED_MIN_LEVEL,
+                                                i);
+
        thermal->tzdev = thermal_zone_device_register("mlxsw",
                                                      MLXSW_THERMAL_NUM_TRIPS,
                                                      MLXSW_THERMAL_TRIP_MASK,
index 5890fdf..66b8098 100644 (file)
@@ -1720,7 +1720,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        const char *driver_name = pdev->driver->name;
        struct mlxsw_pci *mlxsw_pci;
-       bool called_again = false;
        int err;
 
        mlxsw_pci = kzalloc(sizeof(*mlxsw_pci), GFP_KERNEL);
@@ -1777,18 +1776,10 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        mlxsw_pci->bus_info.dev = &pdev->dev;
        mlxsw_pci->id = id;
 
-again:
        err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info,
                                             &mlxsw_pci_bus, mlxsw_pci, false,
                                             NULL);
-       /* -EAGAIN is returned in case the FW was updated. FW needs
-        * a reset, so lets try to call mlxsw_core_bus_device_register()
-        * again.
-        */
-       if (err == -EAGAIN && !called_again) {
-               called_again = true;
-               goto again;
-       } else if (err) {
+       if (err) {
                dev_err(&pdev->dev, "cannot register bus device\n");
                goto err_bus_device_register;
        }
index db3d279..b9c37b0 100644 (file)
@@ -641,6 +641,10 @@ enum mlxsw_reg_sfn_rec_type {
        MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7,
        /* Aged-out MAC address on a LAG port. */
        MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG = 0x8,
+       /* Learned unicast tunnel record. */
+       MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL = 0xD,
+       /* Aged-out unicast tunnel record. */
+       MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL = 0xE,
 };
 
 /* reg_sfn_rec_type
@@ -704,6 +708,66 @@ static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index,
        *p_lag_id = mlxsw_reg_sfn_mac_lag_lag_id_get(payload, rec_index);
 }
 
+/* reg_sfn_uc_tunnel_uip_msb
+ * When protocol is IPv4, the most significant byte of the underlay IPv4
+ * address of the remote VTEP.
+ * When protocol is IPv6, reserved.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_uip_msb, MLXSW_REG_SFN_BASE_LEN, 24,
+                    8, MLXSW_REG_SFN_REC_LEN, 0x08, false);
+
+enum mlxsw_reg_sfn_uc_tunnel_protocol {
+       MLXSW_REG_SFN_UC_TUNNEL_PROTOCOL_IPV4,
+       MLXSW_REG_SFN_UC_TUNNEL_PROTOCOL_IPV6,
+};
+
+/* reg_sfn_uc_tunnel_protocol
+ * IP protocol.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_protocol, MLXSW_REG_SFN_BASE_LEN, 27,
+                    1, MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+/* reg_sfn_uc_tunnel_uip_lsb
+ * When protocol is IPv4, the least significant bytes of the underlay
+ * IPv4 address of the remote VTEP.
+ * When protocol is IPv6, ipv6_id to be queried from TNIPSD.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, uc_tunnel_uip_lsb, MLXSW_REG_SFN_BASE_LEN, 0,
+                    24, MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+enum mlxsw_reg_sfn_tunnel_port {
+       MLXSW_REG_SFN_TUNNEL_PORT_NVE,
+       MLXSW_REG_SFN_TUNNEL_PORT_VPLS,
+       MLXSW_REG_SFN_TUNNEL_FLEX_TUNNEL0,
+       MLXSW_REG_SFN_TUNNEL_FLEX_TUNNEL1,
+};
+
+/* reg_sfn_uc_tunnel_port
+ * Tunnel port.
+ * Reserved on Spectrum.
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, tunnel_port, MLXSW_REG_SFN_BASE_LEN, 0, 4,
+                    MLXSW_REG_SFN_REC_LEN, 0x10, false);
+
+static inline void
+mlxsw_reg_sfn_uc_tunnel_unpack(char *payload, int rec_index, char *mac,
+                              u16 *p_fid, u32 *p_uip,
+                              enum mlxsw_reg_sfn_uc_tunnel_protocol *p_proto)
+{
+       u32 uip_msb, uip_lsb;
+
+       mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac);
+       *p_fid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index);
+       uip_msb = mlxsw_reg_sfn_uc_tunnel_uip_msb_get(payload, rec_index);
+       uip_lsb = mlxsw_reg_sfn_uc_tunnel_uip_lsb_get(payload, rec_index);
+       *p_uip = uip_msb << 24 | uip_lsb;
+       *p_proto = mlxsw_reg_sfn_uc_tunnel_protocol_get(payload, rec_index);
+}
+
 /* SPMS - Switch Port MSTP/RSTP State Register
  * -------------------------------------------
  * Configures the spanning tree state of a physical port.
@@ -2834,8 +2898,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
                                        u32 priority,
                                        const char *tcam_region_info,
                                        const char *key, u8 erp_id,
-                                       bool large_exists, u32 lkey_id,
-                                       u32 action_pointer)
+                                       u16 delta_start, u8 delta_mask,
+                                       u8 delta_value, bool large_exists,
+                                       u32 lkey_id, u32 action_pointer)
 {
        MLXSW_REG_ZERO(ptce3, payload);
        mlxsw_reg_ptce3_v_set(payload, valid);
@@ -2844,6 +2909,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
        mlxsw_reg_ptce3_tcam_region_info_memcpy_to(payload, tcam_region_info);
        mlxsw_reg_ptce3_flex2_key_blocks_memcpy_to(payload, key);
        mlxsw_reg_ptce3_erp_id_set(payload, erp_id);
+       mlxsw_reg_ptce3_delta_start_set(payload, delta_start);
+       mlxsw_reg_ptce3_delta_mask_set(payload, delta_mask);
+       mlxsw_reg_ptce3_delta_value_set(payload, delta_value);
        mlxsw_reg_ptce3_large_exists_set(payload, large_exists);
        mlxsw_reg_ptce3_large_entry_key_id_set(payload, lkey_id);
        mlxsw_reg_ptce3_action_pointer_set(payload, action_pointer);
@@ -4231,8 +4299,11 @@ MLXSW_ITEM32(reg, ppcnt, pnat, 0x00, 14, 2);
 
 enum mlxsw_reg_ppcnt_grp {
        MLXSW_REG_PPCNT_IEEE_8023_CNT = 0x0,
+       MLXSW_REG_PPCNT_RFC_2863_CNT = 0x1,
        MLXSW_REG_PPCNT_RFC_2819_CNT = 0x2,
+       MLXSW_REG_PPCNT_RFC_3635_CNT = 0x3,
        MLXSW_REG_PPCNT_EXT_CNT = 0x5,
+       MLXSW_REG_PPCNT_DISCARD_CNT = 0x6,
        MLXSW_REG_PPCNT_PRIO_CNT = 0x10,
        MLXSW_REG_PPCNT_TC_CNT = 0x11,
        MLXSW_REG_PPCNT_TC_CONG_TC = 0x13,
@@ -4247,6 +4318,7 @@ enum mlxsw_reg_ppcnt_grp {
  * 0x2: RFC 2819 Counters
  * 0x3: RFC 3635 Counters
  * 0x5: Ethernet Extended Counters
+ * 0x6: Ethernet Discard Counters
  * 0x8: Link Level Retransmission Counters
  * 0x10: Per Priority Counters
  * 0x11: Per Traffic Class Counters
@@ -4390,8 +4462,46 @@ MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_received,
 MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_transmitted,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x90, 0, 64);
 
+/* Ethernet RFC 2863 Counter Group */
+
+/* reg_ppcnt_if_in_discards
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_in_discards,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x10, 0, 64);
+
+/* reg_ppcnt_if_out_discards
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_out_discards,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x38, 0, 64);
+
+/* reg_ppcnt_if_out_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, if_out_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
 /* Ethernet RFC 2819 Counter Group */
 
+/* reg_ppcnt_ether_stats_undersize_pkts
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_undersize_pkts,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x30, 0, 64);
+
+/* reg_ppcnt_ether_stats_oversize_pkts
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_oversize_pkts,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x38, 0, 64);
+
+/* reg_ppcnt_ether_stats_fragments
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ether_stats_fragments,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
 /* reg_ppcnt_ether_stats_pkts64octets
  * Access: RO
  */
@@ -4452,6 +4562,32 @@ MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts4096to8191octets,
 MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts8192to10239octets,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0xA0, 0, 64);
 
+/* Ethernet RFC 3635 Counter Group */
+
+/* reg_ppcnt_dot3stats_fcs_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3stats_fcs_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
+
+/* reg_ppcnt_dot3stats_symbol_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3stats_symbol_errors,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x60, 0, 64);
+
+/* reg_ppcnt_dot3control_in_unknown_opcodes
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3control_in_unknown_opcodes,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x68, 0, 64);
+
+/* reg_ppcnt_dot3in_pause_frames
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, dot3in_pause_frames,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64);
+
 /* Ethernet Extended Counter Group Counters */
 
 /* reg_ppcnt_ecn_marked
@@ -4460,6 +4596,80 @@ MLXSW_ITEM64(reg, ppcnt, ether_stats_pkts8192to10239octets,
 MLXSW_ITEM64(reg, ppcnt, ecn_marked,
             MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
 
+/* Ethernet Discard Counter Group Counters */
+
+/* reg_ppcnt_ingress_general
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_general,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x00, 0, 64);
+
+/* reg_ppcnt_ingress_policy_engine
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_policy_engine,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64);
+
+/* reg_ppcnt_ingress_vlan_membership
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_vlan_membership,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x10, 0, 64);
+
+/* reg_ppcnt_ingress_tag_frame_type
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_tag_frame_type,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x18, 0, 64);
+
+/* reg_ppcnt_egress_vlan_membership
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_vlan_membership,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x20, 0, 64);
+
+/* reg_ppcnt_loopback_filter
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, loopback_filter,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x28, 0, 64);
+
+/* reg_ppcnt_egress_general
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_general,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x30, 0, 64);
+
+/* reg_ppcnt_egress_hoq
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_hoq,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x40, 0, 64);
+
+/* reg_ppcnt_egress_policy_engine
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_policy_engine,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x50, 0, 64);
+
+/* reg_ppcnt_ingress_tx_link_down
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, ingress_tx_link_down,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x58, 0, 64);
+
+/* reg_ppcnt_egress_stp_filter
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_stp_filter,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x60, 0, 64);
+
+/* reg_ppcnt_egress_sll
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, egress_sll,
+            MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64);
+
 /* Ethernet Per Priority Group Counters */
 
 /* reg_ppcnt_rx_octets
@@ -4862,6 +5072,7 @@ enum mlxsw_reg_htgt_trap_group {
        MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT,
        MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD,
        MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND,
+       MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR,
 };
 
 /* reg_htgt_trap_group
index 9bec940..0f37b20 100644 (file)
@@ -318,6 +318,7 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
        const struct mlxsw_fw_rev *rev = &mlxsw_sp->bus_info->fw_rev;
        const struct mlxsw_fw_rev *req_rev = mlxsw_sp->req_rev;
        const char *fw_filename = mlxsw_sp->fw_filename;
+       union devlink_param_value value;
        const struct firmware *firmware;
        int err;
 
@@ -325,6 +326,15 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
        if (!req_rev || !fw_filename)
                return 0;
 
+       /* Don't check if devlink 'fw_load_policy' param is 'flash' */
+       err = devlink_param_driverinit_value_get(priv_to_devlink(mlxsw_sp->core),
+                                                DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+                                                &value);
+       if (err)
+               return err;
+       if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)
+               return 0;
+
        /* Validate driver & FW are compatible */
        if (rev->major != req_rev->major) {
                WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n",
@@ -1876,8 +1886,38 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
 
 #define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
 
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = {
+       {
+               .str = "if_in_discards",
+               .getter = mlxsw_reg_ppcnt_if_in_discards_get,
+       },
+       {
+               .str = "if_out_discards",
+               .getter = mlxsw_reg_ppcnt_if_out_discards_get,
+       },
+       {
+               .str = "if_out_errors",
+               .getter = mlxsw_reg_ppcnt_if_out_errors_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats)
+
 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
        {
+               .str = "ether_stats_undersize_pkts",
+               .getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get,
+       },
+       {
+               .str = "ether_stats_oversize_pkts",
+               .getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get,
+       },
+       {
+               .str = "ether_stats_fragments",
+               .getter = mlxsw_reg_ppcnt_ether_stats_fragments_get,
+       },
+       {
                .str = "ether_pkts64octets",
                .getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get,
        },
@@ -1922,6 +1962,82 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
 #define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \
        ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats)
 
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = {
+       {
+               .str = "dot3stats_fcs_errors",
+               .getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get,
+       },
+       {
+               .str = "dot3stats_symbol_errors",
+               .getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get,
+       },
+       {
+               .str = "dot3control_in_unknown_opcodes",
+               .getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get,
+       },
+       {
+               .str = "dot3in_pause_frames",
+               .getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = {
+       {
+               .str = "discard_ingress_general",
+               .getter = mlxsw_reg_ppcnt_ingress_general_get,
+       },
+       {
+               .str = "discard_ingress_policy_engine",
+               .getter = mlxsw_reg_ppcnt_ingress_policy_engine_get,
+       },
+       {
+               .str = "discard_ingress_vlan_membership",
+               .getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get,
+       },
+       {
+               .str = "discard_ingress_tag_frame_type",
+               .getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get,
+       },
+       {
+               .str = "discard_egress_vlan_membership",
+               .getter = mlxsw_reg_ppcnt_egress_vlan_membership_get,
+       },
+       {
+               .str = "discard_loopback_filter",
+               .getter = mlxsw_reg_ppcnt_loopback_filter_get,
+       },
+       {
+               .str = "discard_egress_general",
+               .getter = mlxsw_reg_ppcnt_egress_general_get,
+       },
+       {
+               .str = "discard_egress_hoq",
+               .getter = mlxsw_reg_ppcnt_egress_hoq_get,
+       },
+       {
+               .str = "discard_egress_policy_engine",
+               .getter = mlxsw_reg_ppcnt_egress_policy_engine_get,
+       },
+       {
+               .str = "discard_ingress_tx_link_down",
+               .getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get,
+       },
+       {
+               .str = "discard_egress_stp_filter",
+               .getter = mlxsw_reg_ppcnt_egress_stp_filter_get,
+       },
+       {
+               .str = "discard_egress_sll",
+               .getter = mlxsw_reg_ppcnt_egress_sll_get,
+       },
+};
+
+#define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \
+       ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats)
+
 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
        {
                .str = "rx_octets_prio",
@@ -1974,7 +2090,10 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
 #define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
 
 #define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \
                                         MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \
+                                        MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \
                                         (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
                                          IEEE_8021QAZ_MAX_TCS) + \
                                         (MLXSW_SP_PORT_HW_TC_STATS_LEN * \
@@ -2015,12 +2134,31 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
+
+               for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
                for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) {
                        memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str,
                               ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
 
+               for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) {
+                       memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str,
+                              ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+
                for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
                        mlxsw_sp_port_get_prio_strings(&p, i);
 
@@ -2063,10 +2201,22 @@ mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
                *p_hw_stats = mlxsw_sp_port_hw_stats;
                *p_len = MLXSW_SP_PORT_HW_STATS_LEN;
                break;
+       case MLXSW_REG_PPCNT_RFC_2863_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats;
+               *p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+               break;
        case MLXSW_REG_PPCNT_RFC_2819_CNT:
                *p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats;
                *p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
                break;
+       case MLXSW_REG_PPCNT_RFC_3635_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats;
+               *p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+               break;
+       case MLXSW_REG_PPCNT_DISCARD_CNT:
+               *p_hw_stats = mlxsw_sp_port_hw_discard_stats;
+               *p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+               break;
        case MLXSW_REG_PPCNT_PRIO_CNT:
                *p_hw_stats = mlxsw_sp_port_hw_prio_stats;
                *p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
@@ -2116,11 +2266,26 @@ static void mlxsw_sp_port_get_stats(struct net_device *dev,
                                  data, data_index);
        data_index = MLXSW_SP_PORT_HW_STATS_LEN;
 
+       /* RFC 2863 Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+
        /* RFC 2819 Counters */
        __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0,
                                  data, data_index);
        data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
 
+       /* RFC 3635 Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+
+       /* Discard Counters */
+       __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0,
+                                 data, data_index);
+       data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+
        /* Per-Priority Counters */
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
                __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
@@ -3389,10 +3554,10 @@ static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port,
        return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
 }
 
-static void mlxsw_sp_rx_listener_mr_mark_func(struct sk_buff *skb,
+static void mlxsw_sp_rx_listener_l3_mark_func(struct sk_buff *skb,
                                              u8 local_port, void *priv)
 {
-       skb->offload_mr_fwd_mark = 1;
+       skb->offload_l3_fwd_mark = 1;
        skb->offload_fwd_mark = 1;
        return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
 }
@@ -3440,8 +3605,8 @@ out:
        MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action,    \
                _is_ctrl, SP_##_trap_group, DISCARD)
 
-#define MLXSW_SP_RXL_MR_MARK(_trap_id, _action, _trap_group, _is_ctrl) \
-       MLXSW_RXL(mlxsw_sp_rx_listener_mr_mark_func, _trap_id, _action, \
+#define MLXSW_SP_RXL_L3_MARK(_trap_id, _action, _trap_group, _is_ctrl) \
+       MLXSW_RXL(mlxsw_sp_rx_listener_l3_mark_func, _trap_id, _action, \
                _is_ctrl, SP_##_trap_group, DISCARD)
 
 #define MLXSW_SP_EVENTL(_func, _trap_id)               \
@@ -3474,7 +3639,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
        /* L3 traps */
        MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false),
        MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false),
-       MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false),
+       MLXSW_SP_RXL_L3_MARK(LBERROR, MIRROR_TO_CPU, LBERROR, false),
        MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false),
        MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP,
                          false),
@@ -3518,7 +3683,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = {
        MLXSW_SP_RXL_MARK(IPV6_PIM, TRAP_TO_CPU, PIM, false),
        MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false),
        MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
-       MLXSW_SP_RXL_MR_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
+       MLXSW_SP_RXL_L3_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
        /* NVE traps */
        MLXSW_SP_RXL_MARK(NVE_ENCAP_ARP, TRAP_TO_CPU, ARP, false),
 };
@@ -3548,6 +3713,7 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF:
+               case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
                        rate = 128;
                        burst_size = 7;
                        break;
@@ -3633,6 +3799,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
                case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
+               case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
                        priority = 1;
                        tc = 1;
                        break;
@@ -3956,16 +4123,20 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
        mlxsw_sp_kvdl_fini(mlxsw_sp);
 }
 
+/* Per-FID flood tables are used for both "true" 802.1D FIDs and emulated
+ * 802.1Q FIDs
+ */
+#define MLXSW_SP_FID_FLOOD_TABLE_SIZE  (MLXSW_SP_FID_8021D_MAX + \
+                                        VLAN_VID_MASK - 1)
+
 static const struct mlxsw_config_profile mlxsw_sp1_config_profile = {
        .used_max_mid                   = 1,
        .max_mid                        = MLXSW_SP_MID_MAX,
        .used_flood_tables              = 1,
        .used_flood_mode                = 1,
        .flood_mode                     = 3,
-       .max_fid_offset_flood_tables    = 3,
-       .fid_offset_flood_table_size    = VLAN_N_VID - 1,
        .max_fid_flood_tables           = 3,
-       .fid_flood_table_size           = MLXSW_SP_FID_8021D_MAX,
+       .fid_flood_table_size           = MLXSW_SP_FID_FLOOD_TABLE_SIZE,
        .used_max_ib_mc                 = 1,
        .max_ib_mc                      = 0,
        .used_max_pkey                  = 1,
@@ -3988,10 +4159,8 @@ static const struct mlxsw_config_profile mlxsw_sp2_config_profile = {
        .used_flood_tables              = 1,
        .used_flood_mode                = 1,
        .flood_mode                     = 3,
-       .max_fid_offset_flood_tables    = 3,
-       .fid_offset_flood_table_size    = VLAN_N_VID - 1,
        .max_fid_flood_tables           = 3,
-       .fid_flood_table_size           = MLXSW_SP_FID_8021D_MAX,
+       .fid_flood_table_size           = MLXSW_SP_FID_FLOOD_TABLE_SIZE,
        .used_max_ib_mc                 = 1,
        .max_ib_mc                      = 0,
        .used_max_pkey                  = 1,
@@ -4171,6 +4340,52 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
        return 0;
 }
 
+static int
+mlxsw_sp_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,
+                                              union devlink_param_value val,
+                                              struct netlink_ext_ack *extack)
+{
+       if ((val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER) &&
+           (val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)) {
+               NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct devlink_param mlxsw_sp_devlink_params[] = {
+       DEVLINK_PARAM_GENERIC(FW_LOAD_POLICY,
+                             BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
+                             NULL, NULL,
+                             mlxsw_sp_devlink_param_fw_load_policy_validate),
+};
+
+static int mlxsw_sp_params_register(struct mlxsw_core *mlxsw_core)
+{
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+       union devlink_param_value value;
+       int err;
+
+       err = devlink_params_register(devlink, mlxsw_sp_devlink_params,
+                                     ARRAY_SIZE(mlxsw_sp_devlink_params));
+       if (err)
+               return err;
+
+       value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER;
+       devlink_param_driverinit_value_set(devlink,
+                                          DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+                                          value);
+       return 0;
+}
+
+static void mlxsw_sp_params_unregister(struct mlxsw_core *mlxsw_core)
+{
+       devlink_params_unregister(priv_to_devlink(mlxsw_core),
+                                 mlxsw_sp_devlink_params,
+                                 ARRAY_SIZE(mlxsw_sp_devlink_params));
+}
+
 static struct mlxsw_driver mlxsw_sp1_driver = {
        .kind                           = mlxsw_sp1_driver_name,
        .priv_size                      = sizeof(struct mlxsw_sp),
@@ -4192,6 +4407,8 @@ static struct mlxsw_driver mlxsw_sp1_driver = {
        .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp1_resources_register,
        .kvd_sizes_get                  = mlxsw_sp_kvd_sizes_get,
+       .params_register                = mlxsw_sp_params_register,
+       .params_unregister              = mlxsw_sp_params_unregister,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp1_config_profile,
        .res_query_enabled              = true,
@@ -4217,6 +4434,8 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
        .sb_occ_tc_port_bind_get        = mlxsw_sp_sb_occ_tc_port_bind_get,
        .txhdr_construct                = mlxsw_sp_txhdr_construct,
        .resources_register             = mlxsw_sp2_resources_register,
+       .params_register                = mlxsw_sp_params_register,
+       .params_unregister              = mlxsw_sp_params_unregister,
        .txhdr_len                      = MLXSW_TXHDR_LEN,
        .profile                        = &mlxsw_sp2_config_profile,
        .res_query_enabled              = true,
@@ -4625,6 +4844,30 @@ static bool mlxsw_sp_bridge_has_multiple_vxlans(struct net_device *br_dev)
        return num_vxlans > 1;
 }
 
+static bool mlxsw_sp_bridge_vxlan_vlan_is_valid(struct net_device *br_dev)
+{
+       DECLARE_BITMAP(vlans, VLAN_N_VID) = {0};
+       struct net_device *dev;
+       struct list_head *iter;
+
+       netdev_for_each_lower_dev(br_dev, dev, iter) {
+               u16 pvid;
+               int err;
+
+               if (!netif_is_vxlan(dev))
+                       continue;
+
+               err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
+               if (err || !pvid)
+                       continue;
+
+               if (test_and_set_bit(pvid, vlans))
+                       return false;
+       }
+
+       return true;
+}
+
 static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
                                           struct netlink_ext_ack *extack)
 {
@@ -4633,13 +4876,15 @@ static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
                return false;
        }
 
-       if (br_vlan_enabled(br_dev)) {
-               NL_SET_ERR_MSG_MOD(extack, "VLAN filtering can not be enabled on a bridge with a VxLAN device");
+       if (!br_vlan_enabled(br_dev) &&
+           mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
+               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
                return false;
        }
 
-       if (mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
-               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
+       if (br_vlan_enabled(br_dev) &&
+           !mlxsw_sp_bridge_vxlan_vlan_is_valid(br_dev)) {
+               NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices cannot have the same VLAN as PVID and egress untagged");
                return false;
        }
 
@@ -5014,10 +5259,21 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                if (cu_info->linking) {
                        if (!netif_running(dev))
                                return 0;
+                       /* When the bridge is VLAN-aware, the VNI of the VxLAN
+                        * device needs to be mapped to a VLAN, but at this
+                        * point no VLANs are configured on the VxLAN device
+                        */
+                       if (br_vlan_enabled(upper_dev))
+                               return 0;
                        return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev,
-                                                         dev, extack);
+                                                         dev, 0, extack);
                } else {
-                       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
+                       /* VLANs were already flushed, which triggered the
+                        * necessary cleanup
+                        */
+                       if (br_vlan_enabled(upper_dev))
+                               return 0;
+                       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
                }
                break;
        case NETDEV_PRE_UP:
@@ -5028,7 +5284,7 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                        return 0;
                if (!mlxsw_sp_lower_get(upper_dev))
                        return 0;
-               return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev,
+               return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev, 0,
                                                  extack);
        case NETDEV_DOWN:
                upper_dev = netdev_master_upper_dev_get(dev);
@@ -5038,7 +5294,7 @@ static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
                        return 0;
                if (!mlxsw_sp_lower_get(upper_dev))
                        return 0;
-               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, upper_dev, dev);
+               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
                break;
        }
 
index 0875a79..2d8f369 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/netdevice.h>
 #include <linux/rhashtable.h>
 #include <linux/bitops.h>
+#include <linux/if_bridge.h>
 #include <linux/if_vlan.h>
 #include <linux/list.h>
 #include <linux/dcbnl.h>
@@ -80,6 +81,10 @@ enum mlxsw_sp_fid_type {
        MLXSW_SP_FID_TYPE_MAX,
 };
 
+enum mlxsw_sp_nve_type {
+       MLXSW_SP_NVE_TYPE_VXLAN,
+};
+
 struct mlxsw_sp_mid {
        struct list_head list;
        unsigned char addr[ETH_ALEN];
@@ -261,6 +266,26 @@ static inline bool mlxsw_sp_bridge_has_vxlan(struct net_device *br_dev)
        return !!mlxsw_sp_bridge_vxlan_dev_find(br_dev);
 }
 
+static inline int
+mlxsw_sp_vxlan_mapped_vid(const struct net_device *vxlan_dev, u16 *p_vid)
+{
+       struct bridge_vlan_info vinfo;
+       u16 vid = 0;
+       int err;
+
+       err = br_vlan_get_pvid(vxlan_dev, &vid);
+       if (err || !vid)
+               goto out;
+
+       err = br_vlan_get_info(vxlan_dev, vid, &vinfo);
+       if (err || !(vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED))
+               vid = 0;
+
+out:
+       *p_vid = vid;
+       return err;
+}
+
 static inline bool
 mlxsw_sp_port_is_pause_en(const struct mlxsw_sp_port *mlxsw_sp_port)
 {
@@ -358,11 +383,11 @@ bool mlxsw_sp_bridge_device_is_offloaded(const struct mlxsw_sp *mlxsw_sp,
                                         const struct net_device *br_dev);
 int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
                               const struct net_device *br_dev,
-                              const struct net_device *vxlan_dev,
+                              const struct net_device *vxlan_dev, u16 vid,
                               struct netlink_ext_ack *extack);
 void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
-                                const struct net_device *br_dev,
                                 const struct net_device *vxlan_dev);
+extern struct notifier_block mlxsw_sp_switchdev_notifier;
 
 /* spectrum.c */
 int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -721,6 +746,12 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
                           struct tc_prio_qopt_offload *p);
 
 /* spectrum_fid.c */
+bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid);
+struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
+                                                 u16 fid_index);
+int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex);
+int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
+                         enum mlxsw_sp_nve_type *p_type);
 struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
                                                __be32 vni);
 int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni);
@@ -728,9 +759,12 @@ int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid,
                                     u32 nve_flood_index);
 void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid);
 bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid);
-int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni);
+int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
+                        __be32 vni, int nve_ifindex);
 void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid);
 bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid);
+void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                   const struct net_device *nve_dev);
 int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid,
                           enum mlxsw_sp_flood_type packet_type, u8 local_port,
                           bool member);
@@ -749,6 +783,8 @@ u16 mlxsw_sp_fid_8021q_vid(const struct mlxsw_sp_fid *fid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp,
                                            int br_ifindex);
+struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp,
+                                              u16 vid);
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp,
                                               int br_ifindex);
 struct mlxsw_sp_fid *mlxsw_sp_fid_rfid_get(struct mlxsw_sp *mlxsw_sp,
@@ -797,10 +833,6 @@ extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops;
 extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops;
 
 /* spectrum_nve.c */
-enum mlxsw_sp_nve_type {
-       MLXSW_SP_NVE_TYPE_VXLAN,
-};
-
 struct mlxsw_sp_nve_params {
        enum mlxsw_sp_nve_type type;
        __be32 vni;
@@ -810,6 +842,9 @@ struct mlxsw_sp_nve_params {
 extern const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[];
 extern const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[];
 
+int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
+                                   enum mlxsw_sp_l3proto proto,
+                                   union mlxsw_sp_l3addr *addr);
 int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
                              struct mlxsw_sp_fid *fid,
                              enum mlxsw_sp_l3proto proto,
index 8ca77f3..62e6cf4 100644 (file)
@@ -34,15 +34,15 @@ mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregio
 {
        struct mlxsw_sp_acl_atcam_region *aregion;
        struct mlxsw_sp_acl_atcam_entry *aentry;
-       struct mlxsw_sp_acl_erp *erp;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
 
        aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
        aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 
-       erp = mlxsw_sp_acl_erp_get(aregion, mask, true);
-       if (IS_ERR(erp))
-               return PTR_ERR(erp);
-       aentry->erp = erp;
+       erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, true);
+       if (IS_ERR(erp_mask))
+               return PTR_ERR(erp_mask);
+       aentry->erp_mask = erp_mask;
 
        return 0;
 }
@@ -57,7 +57,7 @@ mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregio
        aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
        aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 
-       mlxsw_sp_acl_erp_put(aregion, aentry->erp);
+       mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
 }
 
 static const struct mlxsw_sp_acl_ctcam_region_ops
index 2dda028..e7bd873 100644 (file)
@@ -14,8 +14,8 @@
 #include "spectrum_acl_tcam.h"
 #include "core_acl_flex_keys.h"
 
-#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_START 6
-#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_END   11
+#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_START   0
+#define MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_END     5
 
 struct mlxsw_sp_acl_atcam_lkey_id_ht_key {
        char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* MSB blocks */
@@ -34,7 +34,7 @@ struct mlxsw_sp_acl_atcam_region_ops {
        void (*fini)(struct mlxsw_sp_acl_atcam_region *aregion);
        struct mlxsw_sp_acl_atcam_lkey_id *
                (*lkey_id_get)(struct mlxsw_sp_acl_atcam_region *aregion,
-                              struct mlxsw_sp_acl_rule_info *rulei, u8 erp_id);
+                              char *enc_key, u8 erp_id);
        void (*lkey_id_put)(struct mlxsw_sp_acl_atcam_region *aregion,
                            struct mlxsw_sp_acl_atcam_lkey_id *lkey_id);
 };
@@ -64,7 +64,7 @@ static const struct rhashtable_params mlxsw_sp_acl_atcam_entries_ht_params = {
 static bool
 mlxsw_sp_acl_atcam_is_centry(const struct mlxsw_sp_acl_atcam_entry *aentry)
 {
-       return mlxsw_sp_acl_erp_is_ctcam_erp(aentry->erp);
+       return mlxsw_sp_acl_erp_mask_is_ctcam(aentry->erp_mask);
 }
 
 static int
@@ -90,8 +90,7 @@ mlxsw_sp_acl_atcam_region_generic_fini(struct mlxsw_sp_acl_atcam_region *aregion
 
 static struct mlxsw_sp_acl_atcam_lkey_id *
 mlxsw_sp_acl_atcam_generic_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                                      struct mlxsw_sp_acl_rule_info *rulei,
-                                      u8 erp_id)
+                                      char *enc_key, u8 erp_id)
 {
        struct mlxsw_sp_acl_atcam_region_generic *region_generic;
 
@@ -220,8 +219,7 @@ mlxsw_sp_acl_atcam_lkey_id_destroy(struct mlxsw_sp_acl_atcam_region *aregion,
 
 static struct mlxsw_sp_acl_atcam_lkey_id *
 mlxsw_sp_acl_atcam_12kb_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                                   struct mlxsw_sp_acl_rule_info *rulei,
-                                   u8 erp_id)
+                                   char *enc_key, u8 erp_id)
 {
        struct mlxsw_sp_acl_atcam_region_12kb *region_12kb = aregion->priv;
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
@@ -230,9 +228,10 @@ mlxsw_sp_acl_atcam_12kb_lkey_id_get(struct mlxsw_sp_acl_atcam_region *aregion,
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
 
-       mlxsw_afk_encode(afk, region->key_info, &rulei->values, ht_key.enc_key,
-                        NULL, MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_START,
-                        MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_END);
+       memcpy(ht_key.enc_key, enc_key, sizeof(ht_key.enc_key));
+       mlxsw_afk_clear(afk, ht_key.enc_key,
+                       MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_START,
+                       MLXSW_SP_ACL_ATCAM_LKEY_ID_BLOCK_CLEAR_END);
        ht_key.erp_id = erp_id;
        lkey_id = rhashtable_lookup_fast(&region_12kb->lkey_ht, &ht_key,
                                         mlxsw_sp_acl_atcam_lkey_id_ht_params);
@@ -379,7 +378,7 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
                                       struct mlxsw_sp_acl_rule_info *rulei)
 {
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
-       u8 erp_id = mlxsw_sp_acl_erp_id(aentry->erp);
+       u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
        char ptce3_pl[MLXSW_REG_PTCE3_LEN];
        u32 kvdl_index, priority;
@@ -389,7 +388,8 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        if (err)
                return err;
 
-       lkey_id = aregion->ops->lkey_id_get(aregion, rulei, erp_id);
+       lkey_id = aregion->ops->lkey_id_get(aregion, aentry->ht_key.enc_key,
+                                           erp_id);
        if (IS_ERR(lkey_id))
                return PTR_ERR(lkey_id);
        aentry->lkey_id = lkey_id;
@@ -398,6 +398,9 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        mlxsw_reg_ptce3_pack(ptce3_pl, true, MLXSW_REG_PTCE3_OP_WRITE_WRITE,
                             priority, region->tcam_region_info,
                             aentry->ht_key.enc_key, erp_id,
+                            aentry->delta_info.start,
+                            aentry->delta_info.mask,
+                            aentry->delta_info.value,
                             refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
                             kvdl_index);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
@@ -418,12 +421,17 @@ mlxsw_sp_acl_atcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id = aentry->lkey_id;
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
-       u8 erp_id = mlxsw_sp_acl_erp_id(aentry->erp);
+       u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
+       char *enc_key = aentry->ht_key.enc_key;
        char ptce3_pl[MLXSW_REG_PTCE3_LEN];
 
        mlxsw_reg_ptce3_pack(ptce3_pl, false, MLXSW_REG_PTCE3_OP_WRITE_WRITE, 0,
-                            region->tcam_region_info, aentry->ht_key.enc_key,
-                            erp_id, refcount_read(&lkey_id->refcnt) != 1,
+                            region->tcam_region_info,
+                            enc_key, erp_id,
+                            aentry->delta_info.start,
+                            aentry->delta_info.mask,
+                            aentry->delta_info.value,
+                            refcount_read(&lkey_id->refcnt) != 1,
                             lkey_id->id, 0);
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
        aregion->ops->lkey_id_put(aregion, lkey_id);
@@ -438,19 +446,30 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_tcam_region *region = aregion->region;
        char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
-       struct mlxsw_sp_acl_erp *erp;
-       unsigned int blocks_count;
+       const struct mlxsw_sp_acl_erp_delta *delta;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
        int err;
 
-       blocks_count = mlxsw_afk_key_info_blocks_count_get(region->key_info);
        mlxsw_afk_encode(afk, region->key_info, &rulei->values,
-                        aentry->ht_key.enc_key, mask, 0, blocks_count - 1);
-
-       erp = mlxsw_sp_acl_erp_get(aregion, mask, false);
-       if (IS_ERR(erp))
-               return PTR_ERR(erp);
-       aentry->erp = erp;
-       aentry->ht_key.erp_id = mlxsw_sp_acl_erp_id(erp);
+                        aentry->full_enc_key, mask);
+
+       erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, false);
+       if (IS_ERR(erp_mask))
+               return PTR_ERR(erp_mask);
+       aentry->erp_mask = erp_mask;
+       aentry->ht_key.erp_id = mlxsw_sp_acl_erp_mask_erp_id(erp_mask);
+       memcpy(aentry->ht_key.enc_key, aentry->full_enc_key,
+              sizeof(aentry->ht_key.enc_key));
+
+       /* Compute all needed delta information and clear the delta bits
+        * from the encrypted key.
+        */
+       delta = mlxsw_sp_acl_erp_delta(aentry->erp_mask);
+       aentry->delta_info.start = mlxsw_sp_acl_erp_delta_start(delta);
+       aentry->delta_info.mask = mlxsw_sp_acl_erp_delta_mask(delta);
+       aentry->delta_info.value =
+               mlxsw_sp_acl_erp_delta_value(delta, aentry->full_enc_key);
+       mlxsw_sp_acl_erp_delta_clear(delta, aentry->ht_key.enc_key);
 
        /* We can't insert identical rules into the A-TCAM, so fail and
         * let the rule spill into C-TCAM
@@ -472,7 +491,7 @@ err_rule_insert:
        rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
                               mlxsw_sp_acl_atcam_entries_ht_params);
 err_rhashtable_insert:
-       mlxsw_sp_acl_erp_put(aregion, erp);
+       mlxsw_sp_acl_erp_mask_put(aregion, erp_mask);
        return err;
 }
 
@@ -484,7 +503,7 @@ __mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
        mlxsw_sp_acl_atcam_region_entry_remove(mlxsw_sp, aregion, aentry);
        rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
                               mlxsw_sp_acl_atcam_entries_ht_params);
-       mlxsw_sp_acl_erp_put(aregion, aentry->erp);
+       mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
 }
 
 int mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
index e3c6fe8..f3e834b 100644 (file)
@@ -46,7 +46,6 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_acl_tcam_region *region = cregion->region;
        struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
        char ptce2_pl[MLXSW_REG_PTCE2_LEN];
-       unsigned int blocks_count;
        char *act_set;
        u32 priority;
        char *mask;
@@ -63,9 +62,7 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
                             centry->parman_item.index, priority);
        key = mlxsw_reg_ptce2_flex_key_blocks_data(ptce2_pl);
        mask = mlxsw_reg_ptce2_mask_data(ptce2_pl);
-       blocks_count = mlxsw_afk_key_info_blocks_count_get(region->key_info);
-       mlxsw_afk_encode(afk, region->key_info, &rulei->values, key, mask, 0,
-                        blocks_count - 1);
+       mlxsw_afk_encode(afk, region->key_info, &rulei->values, key, mask);
 
        err = cregion->ops->entry_insert(cregion, centry, mask);
        if (err)
index 0a4fd3c..d9a4b7e 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/gfp.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
-#include <linux/rhashtable.h>
+#include <linux/objagg.h>
 #include <linux/rtnetlink.h>
 #include <linux/slab.h>
 
@@ -29,6 +29,8 @@ struct mlxsw_sp_acl_erp_core {
 
 struct mlxsw_sp_acl_erp_key {
        char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
+#define __MASK_LEN 0x38
+#define __MASK_IDX(i) (__MASK_LEN - (i) - 1)
        bool ctcam;
 };
 
@@ -36,10 +38,8 @@ struct mlxsw_sp_acl_erp {
        struct mlxsw_sp_acl_erp_key key;
        u8 id;
        u8 index;
-       refcount_t refcnt;
        DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        struct list_head list;
-       struct rhash_head ht_node;
        struct mlxsw_sp_acl_erp_table *erp_table;
 };
 
@@ -53,7 +53,6 @@ struct mlxsw_sp_acl_erp_table {
        DECLARE_BITMAP(erp_id_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
        DECLARE_BITMAP(erp_index_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
        struct list_head atcam_erps_list;
-       struct rhashtable erp_ht;
        struct mlxsw_sp_acl_erp_core *erp_core;
        struct mlxsw_sp_acl_atcam_region *aregion;
        const struct mlxsw_sp_acl_erp_table_ops *ops;
@@ -61,12 +60,8 @@ struct mlxsw_sp_acl_erp_table {
        unsigned int num_atcam_erps;
        unsigned int num_max_atcam_erps;
        unsigned int num_ctcam_erps;
-};
-
-static const struct rhashtable_params mlxsw_sp_acl_erp_ht_params = {
-       .key_len = sizeof(struct mlxsw_sp_acl_erp_key),
-       .key_offset = offsetof(struct mlxsw_sp_acl_erp, key),
-       .head_offset = offsetof(struct mlxsw_sp_acl_erp, ht_node),
+       unsigned int num_deltas;
+       struct objagg *objagg;
 };
 
 struct mlxsw_sp_acl_erp_table_ops {
@@ -119,16 +114,6 @@ static const struct mlxsw_sp_acl_erp_table_ops erp_no_mask_ops = {
        .erp_destroy = mlxsw_sp_acl_erp_no_mask_destroy,
 };
 
-bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp)
-{
-       return erp->key.ctcam;
-}
-
-u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp)
-{
-       return erp->id;
-}
-
 static unsigned int
 mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table *erp_table)
 {
@@ -194,12 +179,15 @@ mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table *erp_table)
 
 static int
 mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
-                                const struct mlxsw_sp_acl_erp *erp)
+                                struct mlxsw_sp_acl_erp_key *key)
 {
+       DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        unsigned long bit;
        int err;
 
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       bitmap_from_arr32(mask_bitmap, (u32 *) key->mask,
+                         MLXSW_SP_ACL_TCAM_MASK_LEN);
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_set(bit,
                                                     &erp_table->master_mask);
 
@@ -210,7 +198,7 @@ mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
        return 0;
 
 err_master_mask_update:
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
                                                       &erp_table->master_mask);
        return err;
@@ -218,12 +206,15 @@ err_master_mask_update:
 
 static int
 mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
-                                  const struct mlxsw_sp_acl_erp *erp)
+                                  struct mlxsw_sp_acl_erp_key *key)
 {
+       DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
        unsigned long bit;
        int err;
 
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       bitmap_from_arr32(mask_bitmap, (u32 *) key->mask,
+                         MLXSW_SP_ACL_TCAM_MASK_LEN);
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
                                                       &erp_table->master_mask);
 
@@ -234,7 +225,7 @@ mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
        return 0;
 
 err_master_mask_update:
-       for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
+       for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
                mlxsw_sp_acl_erp_master_mask_bit_set(bit,
                                                     &erp_table->master_mask);
        return err;
@@ -256,26 +247,16 @@ mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
                goto err_erp_id_get;
 
        memcpy(&erp->key, key, sizeof(*key));
-       bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
-                         MLXSW_SP_ACL_TCAM_MASK_LEN);
        list_add(&erp->list, &erp_table->atcam_erps_list);
-       refcount_set(&erp->refcnt, 1);
        erp_table->num_atcam_erps++;
        erp->erp_table = erp_table;
 
-       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &erp->key);
        if (err)
                goto err_master_mask_set;
 
-       err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
-                                    mlxsw_sp_acl_erp_ht_params);
-       if (err)
-               goto err_rhashtable_insert;
-
        return erp;
 
-err_rhashtable_insert:
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
 err_master_mask_set:
        erp_table->num_atcam_erps--;
        list_del(&erp->list);
@@ -290,9 +271,7 @@ mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp)
 {
        struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
 
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
        erp_table->num_atcam_erps--;
        list_del(&erp->list);
        mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
@@ -647,9 +626,56 @@ mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table *erp_table)
        mlxsw_sp_acl_erp_table_enable(erp_table, false);
 }
 
+static int
+__mlxsw_sp_acl_erp_table_other_inc(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  unsigned int *inc_num)
+{
+       int err;
+
+       /* If there are C-TCAM eRP or deltas in use we need to transition
+        * the region to use eRP table, if it is not already done
+        */
+       if (erp_table->ops != &erp_two_masks_ops &&
+           erp_table->ops != &erp_multiple_masks_ops) {
+               err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
+               if (err)
+                       return err;
+       }
+
+       /* When C-TCAM or deltas are used, the eRP table must be used */
+       if (erp_table->ops != &erp_multiple_masks_ops)
+               erp_table->ops = &erp_multiple_masks_ops;
+
+       (*inc_num)++;
+
+       return 0;
+}
+
+static int mlxsw_sp_acl_erp_ctcam_inc(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       return __mlxsw_sp_acl_erp_table_other_inc(erp_table,
+                                                 &erp_table->num_ctcam_erps);
+}
+
+static int mlxsw_sp_acl_erp_delta_inc(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       return __mlxsw_sp_acl_erp_table_other_inc(erp_table,
+                                                 &erp_table->num_deltas);
+}
+
 static void
-mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
+__mlxsw_sp_acl_erp_table_other_dec(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  unsigned int *dec_num)
 {
+       (*dec_num)--;
+
+       /* If there are no C-TCAM eRP or deltas in use, the state we
+        * transition to depends on the number of A-TCAM eRPs currently
+        * in use.
+        */
+       if (erp_table->num_ctcam_erps > 0 || erp_table->num_deltas > 0)
+               return;
+
        switch (erp_table->num_atcam_erps) {
        case 2:
                /* Keep using the eRP table, but correctly set the
@@ -683,9 +709,21 @@ mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
        }
 }
 
+static void mlxsw_sp_acl_erp_ctcam_dec(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       __mlxsw_sp_acl_erp_table_other_dec(erp_table,
+                                          &erp_table->num_ctcam_erps);
+}
+
+static void mlxsw_sp_acl_erp_delta_dec(struct mlxsw_sp_acl_erp_table *erp_table)
+{
+       __mlxsw_sp_acl_erp_table_other_dec(erp_table,
+                                          &erp_table->num_deltas);
+}
+
 static struct mlxsw_sp_acl_erp *
-__mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
-                                    struct mlxsw_sp_acl_erp_key *key)
+mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
+                                  struct mlxsw_sp_acl_erp_key *key)
 {
        struct mlxsw_sp_acl_erp *erp;
        int err;
@@ -697,89 +735,41 @@ __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
        memcpy(&erp->key, key, sizeof(*key));
        bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
                          MLXSW_SP_ACL_TCAM_MASK_LEN);
-       refcount_set(&erp->refcnt, 1);
-       erp_table->num_ctcam_erps++;
-       erp->erp_table = erp_table;
 
-       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
+       err = mlxsw_sp_acl_erp_ctcam_inc(erp_table);
        if (err)
-               goto err_master_mask_set;
+               goto err_erp_ctcam_inc;
 
-       err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
-                                    mlxsw_sp_acl_erp_ht_params);
+       erp->erp_table = erp_table;
+
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &erp->key);
        if (err)
-               goto err_rhashtable_insert;
+               goto err_master_mask_set;
 
        err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table);
        if (err)
                goto err_erp_region_ctcam_enable;
 
-       /* When C-TCAM is used, the eRP table must be used */
-       erp_table->ops = &erp_multiple_masks_ops;
-
        return erp;
 
 err_erp_region_ctcam_enable:
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-err_rhashtable_insert:
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
 err_master_mask_set:
-       erp_table->num_ctcam_erps--;
+       mlxsw_sp_acl_erp_ctcam_dec(erp_table);
+err_erp_ctcam_inc:
        kfree(erp);
        return ERR_PTR(err);
 }
 
-static struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
-                                  struct mlxsw_sp_acl_erp_key *key)
-{
-       struct mlxsw_sp_acl_erp *erp;
-       int err;
-
-       /* There is a special situation where we need to spill rules
-        * into the C-TCAM, yet the region is still using a master
-        * mask and thus not performing a lookup in the C-TCAM. This
-        * can happen when two rules that only differ in priority - and
-        * thus sharing the same key - are programmed. In this case
-        * we transition the region to use an eRP table
-        */
-       err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
-       if (err)
-               return ERR_PTR(err);
-
-       erp = __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
-       if (IS_ERR(erp)) {
-               err = PTR_ERR(erp);
-               goto err_erp_create;
-       }
-
-       return erp;
-
-err_erp_create:
-       mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
-       return ERR_PTR(err);
-}
-
 static void
 mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp)
 {
        struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
 
        mlxsw_sp_acl_erp_region_ctcam_disable(erp_table);
-       rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
-                              mlxsw_sp_acl_erp_ht_params);
-       mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
-       erp_table->num_ctcam_erps--;
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &erp->key);
+       mlxsw_sp_acl_erp_ctcam_dec(erp_table);
        kfree(erp);
-
-       /* Once the last C-TCAM eRP was destroyed, the state we
-        * transition to depends on the number of A-TCAM eRPs currently
-        * in use
-        */
-       if (erp_table->num_ctcam_erps > 0)
-               return;
-       mlxsw_sp_acl_erp_ctcam_table_ops_set(erp_table);
 }
 
 static struct mlxsw_sp_acl_erp *
@@ -790,7 +780,7 @@ mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
        int err;
 
        if (key->ctcam)
-               return __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
+               return mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
 
        /* Expand the eRP table for the new eRP, if needed */
        err = mlxsw_sp_acl_erp_table_expand(erp_table);
@@ -838,7 +828,8 @@ mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
        mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
        mlxsw_sp_acl_erp_generic_destroy(erp);
 
-       if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0)
+       if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0 &&
+           erp_table->num_deltas == 0)
                erp_table->ops = &erp_two_masks_ops;
 }
 
@@ -940,13 +931,12 @@ mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
        WARN_ON(1);
 }
 
-struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                    const char *mask, bool ctcam)
+struct mlxsw_sp_acl_erp_mask *
+mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
+                         const char *mask, bool ctcam)
 {
-       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
        struct mlxsw_sp_acl_erp_key key;
-       struct mlxsw_sp_acl_erp *erp;
+       struct objagg_obj *objagg_obj;
 
        /* eRPs are allocated from a shared resource, but currently all
         * allocations are done under RTNL.
@@ -955,29 +945,238 @@ mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
 
        memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
        key.ctcam = ctcam;
-       erp = rhashtable_lookup_fast(&erp_table->erp_ht, &key,
-                                    mlxsw_sp_acl_erp_ht_params);
-       if (erp) {
-               refcount_inc(&erp->refcnt);
-               return erp;
+       objagg_obj = objagg_obj_get(aregion->erp_table->objagg, &key);
+       if (IS_ERR(objagg_obj))
+               return ERR_CAST(objagg_obj);
+       return (struct mlxsw_sp_acl_erp_mask *) objagg_obj;
+}
+
+void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion,
+                              struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+
+       ASSERT_RTNL();
+       objagg_obj_put(aregion->erp_table->objagg, objagg_obj);
+}
+
+bool
+mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp_key *key = objagg_obj_raw(objagg_obj);
+
+       return key->ctcam;
+}
+
+u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj);
+
+       return erp->id;
+}
+
+struct mlxsw_sp_acl_erp_delta {
+       struct mlxsw_sp_acl_erp_key key;
+       u16 start;
+       u8 mask;
+};
+
+u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta)
+{
+       return delta->start;
+}
+
+u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta)
+{
+       return delta->mask;
+}
+
+u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
+                               const char *enc_key)
+{
+       u16 start = delta->start;
+       u8 mask = delta->mask;
+       u16 tmp;
+
+       if (!mask)
+               return 0;
+
+       tmp = (unsigned char) enc_key[__MASK_IDX(start / 8)];
+       if (start / 8 + 1 < __MASK_LEN)
+               tmp |= (unsigned char) enc_key[__MASK_IDX(start / 8 + 1)] << 8;
+       tmp >>= start % 8;
+       tmp &= mask;
+       return tmp;
+}
+
+void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
+                                 const char *enc_key)
+{
+       u16 start = delta->start;
+       u8 mask = delta->mask;
+       unsigned char *byte;
+       u16 tmp;
+
+       tmp = mask;
+       tmp <<= start % 8;
+       tmp = ~tmp;
+
+       byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8)];
+       *byte &= tmp & 0xff;
+       if (start / 8 + 1 < __MASK_LEN) {
+               byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8 + 1)];
+               *byte &= (tmp >> 8) & 0xff;
        }
+}
+
+static const struct mlxsw_sp_acl_erp_delta
+mlxsw_sp_acl_erp_delta_default = {};
 
-       return erp_table->ops->erp_create(erp_table, &key);
+const struct mlxsw_sp_acl_erp_delta *
+mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask)
+{
+       struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
+       const struct mlxsw_sp_acl_erp_delta *delta;
+
+       delta = objagg_obj_delta_priv(objagg_obj);
+       if (!delta)
+               delta = &mlxsw_sp_acl_erp_delta_default;
+       return delta;
 }
 
-void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
-                         struct mlxsw_sp_acl_erp *erp)
+static int
+mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key,
+                           const struct mlxsw_sp_acl_erp_key *key,
+                           u16 *delta_start, u8 *delta_mask)
 {
+       int offset = 0;
+       int si = -1;
+       u16 pmask;
+       u16 mask;
+       int i;
+
+       /* The difference between 2 masks can be up to 8 consecutive bits. */
+       for (i = 0; i < __MASK_LEN; i++) {
+               if (parent_key->mask[__MASK_IDX(i)] == key->mask[__MASK_IDX(i)])
+                       continue;
+               if (si == -1)
+                       si = i;
+               else if (si != i - 1)
+                       return -EINVAL;
+       }
+       if (si == -1) {
+               /* The masks are the same, this cannot happen.
+                * That means the caller is broken.
+                */
+               WARN_ON(1);
+               *delta_start = 0;
+               *delta_mask = 0;
+               return 0;
+       }
+       pmask = (unsigned char) parent_key->mask[__MASK_IDX(si)];
+       mask = (unsigned char) key->mask[__MASK_IDX(si)];
+       if (si + 1 < __MASK_LEN) {
+               pmask |= (unsigned char) parent_key->mask[__MASK_IDX(si + 1)] << 8;
+               mask |= (unsigned char) key->mask[__MASK_IDX(si + 1)] << 8;
+       }
+
+       if ((pmask ^ mask) & pmask)
+               return -EINVAL;
+       mask &= ~pmask;
+       while (!(mask & (1 << offset)))
+               offset++;
+       while (!(mask & 1))
+               mask >>= 1;
+       if (mask & 0xff00)
+               return -EINVAL;
+
+       *delta_start = si * 8 + offset;
+       *delta_mask = mask;
+
+       return 0;
+}
+
+static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj,
+                                          void *obj)
+{
+       struct mlxsw_sp_acl_erp_key *parent_key = parent_obj;
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
        struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+       struct mlxsw_sp_acl_erp_key *key = obj;
+       struct mlxsw_sp_acl_erp_delta *delta;
+       u16 delta_start;
+       u8 delta_mask;
+       int err;
 
-       ASSERT_RTNL();
+       if (parent_key->ctcam || key->ctcam)
+               return ERR_PTR(-EINVAL);
+       err = mlxsw_sp_acl_erp_delta_fill(parent_key, key,
+                                         &delta_start, &delta_mask);
+       if (err)
+               return ERR_PTR(-EINVAL);
 
-       if (!refcount_dec_and_test(&erp->refcnt))
-               return;
+       delta = kzalloc(sizeof(*delta), GFP_KERNEL);
+       if (!delta)
+               return ERR_PTR(-ENOMEM);
+       delta->start = delta_start;
+       delta->mask = delta_mask;
+
+       err = mlxsw_sp_acl_erp_delta_inc(erp_table);
+       if (err)
+               goto err_erp_delta_inc;
+
+       memcpy(&delta->key, key, sizeof(*key));
+       err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &delta->key);
+       if (err)
+               goto err_master_mask_set;
+
+       return delta;
 
-       erp_table->ops->erp_destroy(erp_table, erp);
+err_master_mask_set:
+       mlxsw_sp_acl_erp_delta_dec(erp_table);
+err_erp_delta_inc:
+       kfree(delta);
+       return ERR_PTR(err);
 }
 
+static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv)
+{
+       struct mlxsw_sp_acl_erp_delta *delta = delta_priv;
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
+       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+
+       mlxsw_sp_acl_erp_master_mask_clear(erp_table, &delta->key);
+       mlxsw_sp_acl_erp_delta_dec(erp_table);
+       kfree(delta);
+}
+
+static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj)
+{
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
+       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+       struct mlxsw_sp_acl_erp_key *key = obj;
+
+       return erp_table->ops->erp_create(erp_table, key);
+}
+
+static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv)
+{
+       struct mlxsw_sp_acl_atcam_region *aregion = priv;
+       struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
+
+       erp_table->ops->erp_destroy(erp_table, root_priv);
+}
+
+static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
+       .obj_size = sizeof(struct mlxsw_sp_acl_erp_key),
+       .delta_create = mlxsw_sp_acl_erp_delta_create,
+       .delta_destroy = mlxsw_sp_acl_erp_delta_destroy,
+       .root_create = mlxsw_sp_acl_erp_root_create,
+       .root_destroy = mlxsw_sp_acl_erp_root_destroy,
+};
+
 static struct mlxsw_sp_acl_erp_table *
 mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
 {
@@ -988,9 +1187,12 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
        if (!erp_table)
                return ERR_PTR(-ENOMEM);
 
-       err = rhashtable_init(&erp_table->erp_ht, &mlxsw_sp_acl_erp_ht_params);
-       if (err)
-               goto err_rhashtable_init;
+       erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops,
+                                         aregion);
+       if (IS_ERR(erp_table->objagg)) {
+               err = PTR_ERR(erp_table->objagg);
+               goto err_objagg_create;
+       }
 
        erp_table->erp_core = aregion->atcam->erp_core;
        erp_table->ops = &erp_no_mask_ops;
@@ -999,7 +1201,7 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
 
        return erp_table;
 
-err_rhashtable_init:
+err_objagg_create:
        kfree(erp_table);
        return ERR_PTR(err);
 }
@@ -1008,7 +1210,7 @@ static void
 mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table *erp_table)
 {
        WARN_ON(!list_empty(&erp_table->atcam_erps_list));
-       rhashtable_destroy(&erp_table->erp_ht);
+       objagg_destroy(erp_table->objagg);
        kfree(erp_table);
 }
 
index d409b09..2e1e8c4 100644 (file)
@@ -98,8 +98,8 @@ static const struct mlxsw_afk_block mlxsw_sp1_afk_blocks[] = {
 
 #define MLXSW_SP1_AFK_KEY_BLOCK_SIZE 16
 
-static void mlxsw_sp1_afk_encode_block(char *block, int block_index,
-                                      char *output)
+static void mlxsw_sp1_afk_encode_block(char *output, int block_index,
+                                      char *block)
 {
        unsigned int offset = block_index * MLXSW_SP1_AFK_KEY_BLOCK_SIZE;
        char *output_indexed = output + offset;
@@ -107,10 +107,19 @@ static void mlxsw_sp1_afk_encode_block(char *block, int block_index,
        memcpy(output_indexed, block, MLXSW_SP1_AFK_KEY_BLOCK_SIZE);
 }
 
+static void mlxsw_sp1_afk_clear_block(char *output, int block_index)
+{
+       unsigned int offset = block_index * MLXSW_SP1_AFK_KEY_BLOCK_SIZE;
+       char *output_indexed = output + offset;
+
+       memset(output_indexed, 0, MLXSW_SP1_AFK_KEY_BLOCK_SIZE);
+}
+
 const struct mlxsw_afk_ops mlxsw_sp1_afk_ops = {
        .blocks         = mlxsw_sp1_afk_blocks,
        .blocks_count   = ARRAY_SIZE(mlxsw_sp1_afk_blocks),
        .encode_block   = mlxsw_sp1_afk_encode_block,
+       .clear_block    = mlxsw_sp1_afk_clear_block,
 };
 
 static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_mac_0[] = {
@@ -263,10 +272,9 @@ static const struct mlxsw_sp2_afk_block_layout mlxsw_sp2_afk_blocks_layout[] = {
        MLXSW_SP2_AFK_BLOCK_LAYOUT(block11, 0x00, 12),
 };
 
-static void mlxsw_sp2_afk_encode_block(char *block, int block_index,
-                                      char *output)
+static void __mlxsw_sp2_afk_block_value_set(char *output, int block_index,
+                                           u64 block_value)
 {
-       u64 block_value = mlxsw_sp2_afk_block_value_get(block);
        const struct mlxsw_sp2_afk_block_layout *block_layout;
 
        if (WARN_ON(block_index < 0 ||
@@ -278,8 +286,22 @@ static void mlxsw_sp2_afk_encode_block(char *block, int block_index,
                           &block_layout->item, 0, block_value);
 }
 
+static void mlxsw_sp2_afk_encode_block(char *output, int block_index,
+                                      char *block)
+{
+       u64 block_value = mlxsw_sp2_afk_block_value_get(block);
+
+       __mlxsw_sp2_afk_block_value_set(output, block_index, block_value);
+}
+
+static void mlxsw_sp2_afk_clear_block(char *output, int block_index)
+{
+       __mlxsw_sp2_afk_block_value_set(output, block_index, 0);
+}
+
 const struct mlxsw_afk_ops mlxsw_sp2_afk_ops = {
        .blocks         = mlxsw_sp2_afk_blocks,
        .blocks_count   = ARRAY_SIZE(mlxsw_sp2_afk_blocks),
        .encode_block   = mlxsw_sp2_afk_encode_block,
+       .clear_block    = mlxsw_sp2_afk_clear_block,
 };
index 219a4e2..9a73759 100644 (file)
@@ -154,7 +154,9 @@ struct mlxsw_sp_acl_atcam_region {
 };
 
 struct mlxsw_sp_acl_atcam_entry_ht_key {
-       char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key */
+       char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key,
+                                                           * minus delta bits.
+                                                           */
        u8 erp_id;
 };
 
@@ -165,9 +167,15 @@ struct mlxsw_sp_acl_atcam_chunk {
 struct mlxsw_sp_acl_atcam_entry {
        struct rhash_head ht_node;
        struct mlxsw_sp_acl_atcam_entry_ht_key ht_key;
+       char full_enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key */
+       struct {
+               u16 start;
+               u8 mask;
+               u8 value;
+       } delta_info;
        struct mlxsw_sp_acl_ctcam_entry centry;
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
-       struct mlxsw_sp_acl_erp *erp;
+       struct mlxsw_sp_acl_erp_mask *erp_mask;
 };
 
 static inline struct mlxsw_sp_acl_atcam_region *
@@ -209,15 +217,27 @@ int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
 void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
                             struct mlxsw_sp_acl_atcam *atcam);
 
-struct mlxsw_sp_acl_erp;
-
-bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp);
-u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp);
-struct mlxsw_sp_acl_erp *
-mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
-                    const char *mask, bool ctcam);
-void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
-                         struct mlxsw_sp_acl_erp *erp);
+struct mlxsw_sp_acl_erp_delta;
+
+u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta);
+u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta);
+u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
+                               const char *enc_key);
+void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
+                                 const char *enc_key);
+
+struct mlxsw_sp_acl_erp_mask;
+
+bool
+mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+const struct mlxsw_sp_acl_erp_delta *
+mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask);
+struct mlxsw_sp_acl_erp_mask *
+mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
+                         const char *mask, bool ctcam);
+void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion,
+                              struct mlxsw_sp_acl_erp_mask *erp_mask);
 int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion);
 void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
 int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
index a3db033..7adb149 100644 (file)
@@ -15,6 +15,7 @@
 struct mlxsw_sp_fid_family;
 
 struct mlxsw_sp_fid_core {
+       struct rhashtable fid_ht;
        struct rhashtable vni_ht;
        struct mlxsw_sp_fid_family *fid_family_arr[MLXSW_SP_FID_TYPE_MAX];
        unsigned int *port_fid_mappings;
@@ -26,10 +27,13 @@ struct mlxsw_sp_fid {
        unsigned int ref_count;
        u16 fid_index;
        struct mlxsw_sp_fid_family *fid_family;
+       struct rhash_head ht_node;
 
        struct rhash_head vni_ht_node;
+       enum mlxsw_sp_nve_type nve_type;
        __be32 vni;
        u32 nve_flood_index;
+       int nve_ifindex;
        u8 vni_valid:1,
           nve_flood_index_valid:1;
 };
@@ -44,6 +48,12 @@ struct mlxsw_sp_fid_8021d {
        int br_ifindex;
 };
 
+static const struct rhashtable_params mlxsw_sp_fid_ht_params = {
+       .key_len = sizeof_field(struct mlxsw_sp_fid, fid_index),
+       .key_offset = offsetof(struct mlxsw_sp_fid, fid_index),
+       .head_offset = offsetof(struct mlxsw_sp_fid, ht_node),
+};
+
 static const struct rhashtable_params mlxsw_sp_fid_vni_ht_params = {
        .key_len = sizeof_field(struct mlxsw_sp_fid, vni),
        .key_offset = offsetof(struct mlxsw_sp_fid, vni),
@@ -75,6 +85,8 @@ struct mlxsw_sp_fid_ops {
        int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid,
                                   u32 nve_flood_index);
        void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid);
+       void (*fdb_clear_offload)(const struct mlxsw_sp_fid *fid,
+                                 const struct net_device *nve_dev);
 };
 
 struct mlxsw_sp_fid_family {
@@ -89,6 +101,7 @@ struct mlxsw_sp_fid_family {
        enum mlxsw_sp_rif_type rif_type;
        const struct mlxsw_sp_fid_ops *ops;
        struct mlxsw_sp *mlxsw_sp;
+       u8 lag_vid_valid:1;
 };
 
 static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
@@ -113,6 +126,45 @@ static const int *mlxsw_sp_packet_type_sfgc_types[] = {
        [MLXSW_SP_FLOOD_TYPE_MC]        = mlxsw_sp_sfgc_mc_packet_types,
 };
 
+bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid)
+{
+       return fid->fid_family->lag_vid_valid;
+}
+
+struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
+                                                 u16 fid_index)
+{
+       struct mlxsw_sp_fid *fid;
+
+       fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->fid_ht, &fid_index,
+                                    mlxsw_sp_fid_ht_params);
+       if (fid)
+               fid->ref_count++;
+
+       return fid;
+}
+
+int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex)
+{
+       if (!fid->vni_valid)
+               return -EINVAL;
+
+       *nve_ifindex = fid->nve_ifindex;
+
+       return 0;
+}
+
+int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
+                         enum mlxsw_sp_nve_type *p_type)
+{
+       if (!fid->vni_valid)
+               return -EINVAL;
+
+       *p_type = fid->nve_type;
+
+       return 0;
+}
+
 struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
                                                __be32 vni)
 {
@@ -173,7 +225,8 @@ bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid)
        return fid->nve_flood_index_valid;
 }
 
-int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni)
+int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
+                        __be32 vni, int nve_ifindex)
 {
        struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
        const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
@@ -183,6 +236,8 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, __be32 vni)
        if (WARN_ON(!ops->vni_set || fid->vni_valid))
                return -EINVAL;
 
+       fid->nve_type = type;
+       fid->nve_ifindex = nve_ifindex;
        fid->vni = vni;
        err = rhashtable_lookup_insert_fast(&mlxsw_sp->fid_core->vni_ht,
                                            &fid->vni_ht_node,
@@ -224,6 +279,16 @@ bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid)
        return fid->vni_valid;
 }
 
+void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                   const struct net_device *nve_dev)
+{
+       struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
+       const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
+
+       if (ops->fdb_clear_offload)
+               ops->fdb_clear_offload(fid, nve_dev);
+}
+
 static const struct mlxsw_sp_flood_table *
 mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid,
                                enum mlxsw_sp_flood_type packet_type)
@@ -568,7 +633,7 @@ mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg)
 
 static u16 mlxsw_sp_fid_8021d_flood_index(const struct mlxsw_sp_fid *fid)
 {
-       return fid->fid_index - fid->fid_family->start_index;
+       return fid->fid_index - VLAN_N_VID;
 }
 
 static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
@@ -713,6 +778,13 @@ static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
                            fid->vni_valid, 0, false);
 }
 
+static void
+mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                    const struct net_device *nve_dev)
+{
+       br_fdb_clear_offload(nve_dev, 0);
+}
+
 static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
        .setup                  = mlxsw_sp_fid_8021d_setup,
        .configure              = mlxsw_sp_fid_8021d_configure,
@@ -726,6 +798,7 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
        .vni_clear              = mlxsw_sp_fid_8021d_vni_clear,
        .nve_flood_index_set    = mlxsw_sp_fid_8021d_nve_flood_index_set,
        .nve_flood_index_clear  = mlxsw_sp_fid_8021d_nve_flood_index_clear,
+       .fdb_clear_offload      = mlxsw_sp_fid_8021d_fdb_clear_offload,
 };
 
 static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = {
@@ -759,6 +832,48 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = {
        .nr_flood_tables        = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
        .rif_type               = MLXSW_SP_RIF_TYPE_FID,
        .ops                    = &mlxsw_sp_fid_8021d_ops,
+       .lag_vid_valid          = 1,
+};
+
+static void
+mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
+                                    const struct net_device *nve_dev)
+{
+       br_fdb_clear_offload(nve_dev, mlxsw_sp_fid_8021q_vid(fid));
+}
+
+static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = {
+       .setup                  = mlxsw_sp_fid_8021q_setup,
+       .configure              = mlxsw_sp_fid_8021d_configure,
+       .deconfigure            = mlxsw_sp_fid_8021d_deconfigure,
+       .index_alloc            = mlxsw_sp_fid_8021d_index_alloc,
+       .compare                = mlxsw_sp_fid_8021q_compare,
+       .flood_index            = mlxsw_sp_fid_8021d_flood_index,
+       .port_vid_map           = mlxsw_sp_fid_8021d_port_vid_map,
+       .port_vid_unmap         = mlxsw_sp_fid_8021d_port_vid_unmap,
+       .vni_set                = mlxsw_sp_fid_8021d_vni_set,
+       .vni_clear              = mlxsw_sp_fid_8021d_vni_clear,
+       .nve_flood_index_set    = mlxsw_sp_fid_8021d_nve_flood_index_set,
+       .nve_flood_index_clear  = mlxsw_sp_fid_8021d_nve_flood_index_clear,
+       .fdb_clear_offload      = mlxsw_sp_fid_8021q_fdb_clear_offload,
+};
+
+/* There are 4K-2 emulated 802.1Q FIDs, starting right after the 802.1D FIDs */
+#define MLXSW_SP_FID_8021Q_EMU_START   (VLAN_N_VID + MLXSW_SP_FID_8021D_MAX)
+#define MLXSW_SP_FID_8021Q_EMU_END     (MLXSW_SP_FID_8021Q_EMU_START + \
+                                        VLAN_VID_MASK - 2)
+
+/* Range and flood configuration must match mlxsw_config_profile */
+static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021q_emu_family = {
+       .type                   = MLXSW_SP_FID_TYPE_8021Q,
+       .fid_size               = sizeof(struct mlxsw_sp_fid_8021q),
+       .start_index            = MLXSW_SP_FID_8021Q_EMU_START,
+       .end_index              = MLXSW_SP_FID_8021Q_EMU_END,
+       .flood_tables           = mlxsw_sp_fid_8021d_flood_tables,
+       .nr_flood_tables        = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
+       .rif_type               = MLXSW_SP_RIF_TYPE_VLAN,
+       .ops                    = &mlxsw_sp_fid_8021q_emu_ops,
+       .lag_vid_valid          = 1,
 };
 
 static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid)
@@ -888,7 +1003,7 @@ static const struct mlxsw_sp_fid_family mlxsw_sp_fid_dummy_family = {
 };
 
 static const struct mlxsw_sp_fid_family *mlxsw_sp_fid_family_arr[] = {
-       [MLXSW_SP_FID_TYPE_8021Q]       = &mlxsw_sp_fid_8021q_family,
+       [MLXSW_SP_FID_TYPE_8021Q]       = &mlxsw_sp_fid_8021q_emu_family,
        [MLXSW_SP_FID_TYPE_8021D]       = &mlxsw_sp_fid_8021d_family,
        [MLXSW_SP_FID_TYPE_RFID]        = &mlxsw_sp_fid_rfid_family,
        [MLXSW_SP_FID_TYPE_DUMMY]       = &mlxsw_sp_fid_dummy_family,
@@ -944,10 +1059,17 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_configure;
 
+       err = rhashtable_insert_fast(&mlxsw_sp->fid_core->fid_ht, &fid->ht_node,
+                                    mlxsw_sp_fid_ht_params);
+       if (err)
+               goto err_rhashtable_insert;
+
        list_add(&fid->list, &fid_family->fids_list);
        fid->ref_count++;
        return fid;
 
+err_rhashtable_insert:
+       fid->fid_family->ops->deconfigure(fid);
 err_configure:
        __clear_bit(fid_index - fid_family->start_index,
                    fid_family->fids_bitmap);
@@ -959,6 +1081,7 @@ err_index_alloc:
 void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid)
 {
        struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
+       struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
 
        if (--fid->ref_count == 1 && fid->rif) {
                /* Destroy the associated RIF and let it drop the last
@@ -967,6 +1090,8 @@ void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid)
                return mlxsw_sp_rif_destroy(fid->rif);
        } else if (fid->ref_count == 0) {
                list_del(&fid->list);
+               rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
+                                      &fid->ht_node, mlxsw_sp_fid_ht_params);
                fid->fid_family->ops->deconfigure(fid);
                __clear_bit(fid->fid_index - fid_family->start_index,
                            fid_family->fids_bitmap);
@@ -985,6 +1110,12 @@ struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp,
        return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021D, &br_ifindex);
 }
 
+struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp,
+                                              u16 vid)
+{
+       return mlxsw_sp_fid_lookup(mlxsw_sp, MLXSW_SP_FID_TYPE_8021Q, &vid);
+}
+
 struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp,
                                               int br_ifindex)
 {
@@ -1126,9 +1257,13 @@ int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp)
                return -ENOMEM;
        mlxsw_sp->fid_core = fid_core;
 
+       err = rhashtable_init(&fid_core->fid_ht, &mlxsw_sp_fid_ht_params);
+       if (err)
+               goto err_rhashtable_fid_init;
+
        err = rhashtable_init(&fid_core->vni_ht, &mlxsw_sp_fid_vni_ht_params);
        if (err)
-               goto err_rhashtable_init;
+               goto err_rhashtable_vni_init;
 
        fid_core->port_fid_mappings = kcalloc(max_ports, sizeof(unsigned int),
                                              GFP_KERNEL);
@@ -1157,7 +1292,9 @@ err_fid_ops_register:
        kfree(fid_core->port_fid_mappings);
 err_alloc_port_fid_mappings:
        rhashtable_destroy(&fid_core->vni_ht);
-err_rhashtable_init:
+err_rhashtable_vni_init:
+       rhashtable_destroy(&fid_core->fid_ht);
+err_rhashtable_fid_init:
        kfree(fid_core);
        return err;
 }
@@ -1172,5 +1309,6 @@ void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp)
                                               fid_core->fid_family_arr[i]);
        kfree(fid_core->port_fid_mappings);
        rhashtable_destroy(&fid_core->vni_ht);
+       rhashtable_destroy(&fid_core->fid_ht);
        kfree(fid_core);
 }
index 5c13674..3b1e826 100644 (file)
@@ -174,6 +174,20 @@ mlxsw_sp_nve_mc_record_ops_arr[] = {
        [MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
 };
 
+int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
+                                   enum mlxsw_sp_l3proto proto,
+                                   union mlxsw_sp_l3addr *addr)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               addr->addr4 = cpu_to_be32(uip);
+               return 0;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+}
+
 static struct mlxsw_sp_nve_mc_list *
 mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
                          const struct mlxsw_sp_nve_mc_list_key *key)
@@ -775,6 +789,21 @@ static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
 }
 
+static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
+                                          const struct mlxsw_sp_fid *fid,
+                                          const struct net_device *nve_dev,
+                                          __be32 vni)
+{
+       const struct mlxsw_sp_nve_ops *ops;
+       enum mlxsw_sp_nve_type type;
+
+       if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
+               return;
+
+       ops = mlxsw_sp->nve->nve_ops_arr[type];
+       ops->fdb_clear_offload(nve_dev, vni);
+}
+
 int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
                            struct mlxsw_sp_nve_params *params,
                            struct netlink_ext_ack *extack)
@@ -803,7 +832,8 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
                return err;
        }
 
-       err = mlxsw_sp_fid_vni_set(fid, params->vni);
+       err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
+                                  params->dev->ifindex);
        if (err) {
                NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
                goto err_fid_vni_set;
@@ -811,8 +841,16 @@ int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
 
        nve->config = config;
 
+       err = ops->fdb_replay(params->dev, params->vni);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Failed to offload the FDB");
+               goto err_fdb_replay;
+       }
+
        return 0;
 
+err_fdb_replay:
+       mlxsw_sp_fid_vni_clear(fid);
 err_fid_vni_set:
        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
        return err;
@@ -822,9 +860,27 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
                              struct mlxsw_sp_fid *fid)
 {
        u16 fid_index = mlxsw_sp_fid_index(fid);
+       struct net_device *nve_dev;
+       int nve_ifindex;
+       __be32 vni;
 
        mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
        mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
+
+       if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
+                   mlxsw_sp_fid_vni(fid, &vni)))
+               goto out;
+
+       nve_dev = dev_get_by_index(&init_net, nve_ifindex);
+       if (!nve_dev)
+               goto out;
+
+       mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
+       mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
+
+       dev_put(nve_dev);
+
+out:
        mlxsw_sp_fid_vni_clear(fid);
        mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
 }
index 4cc3297..02937ea 100644 (file)
@@ -41,6 +41,8 @@ struct mlxsw_sp_nve_ops {
        int (*init)(struct mlxsw_sp_nve *nve,
                    const struct mlxsw_sp_nve_config *config);
        void (*fini)(struct mlxsw_sp_nve *nve);
+       int (*fdb_replay)(const struct net_device *nve_dev, __be32 vni);
+       void (*fdb_clear_offload)(const struct net_device *nve_dev, __be32 vni);
 };
 
 extern const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops;
index d21c7be..74e564c 100644 (file)
@@ -17,7 +17,8 @@
 #define MLXSW_SP_NVE_VXLAN_PARSING_DEPTH 128
 #define MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH 96
 
-#define MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS     VXLAN_F_UDP_ZERO_CSUM_TX
+#define MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS     (VXLAN_F_UDP_ZERO_CSUM_TX | \
+                                                VXLAN_F_LEARN)
 
 static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
                                            const struct net_device *dev,
@@ -61,11 +62,6 @@ static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
                return false;
        }
 
-       if (cfg->flags & VXLAN_F_LEARN) {
-               NL_SET_ERR_MSG_MOD(extack, "VxLAN: Learning is not supported");
-               return false;
-       }
-
        if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) {
                NL_SET_ERR_MSG_MOD(extack, "VxLAN: UDP checksum is not supported");
                return false;
@@ -215,12 +211,30 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve)
                                 config->udp_dport);
 }
 
+static int
+mlxsw_sp_nve_vxlan_fdb_replay(const struct net_device *nve_dev, __be32 vni)
+{
+       if (WARN_ON(!netif_is_vxlan(nve_dev)))
+               return -EINVAL;
+       return vxlan_fdb_replay(nve_dev, vni, &mlxsw_sp_switchdev_notifier);
+}
+
+static void
+mlxsw_sp_nve_vxlan_clear_offload(const struct net_device *nve_dev, __be32 vni)
+{
+       if (WARN_ON(!netif_is_vxlan(nve_dev)))
+               return;
+       vxlan_fdb_clear_offload(nve_dev, vni);
+}
+
 const struct mlxsw_sp_nve_ops mlxsw_sp1_nve_vxlan_ops = {
        .type           = MLXSW_SP_NVE_TYPE_VXLAN,
        .can_offload    = mlxsw_sp1_nve_vxlan_can_offload,
        .nve_config     = mlxsw_sp_nve_vxlan_config,
        .init           = mlxsw_sp1_nve_vxlan_init,
        .fini           = mlxsw_sp1_nve_vxlan_fini,
+       .fdb_replay     = mlxsw_sp_nve_vxlan_fdb_replay,
+       .fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
 };
 
 static bool mlxsw_sp2_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
@@ -246,4 +260,6 @@ const struct mlxsw_sp_nve_ops mlxsw_sp2_nve_vxlan_ops = {
        .nve_config     = mlxsw_sp_nve_vxlan_config,
        .init           = mlxsw_sp2_nve_vxlan_init,
        .fini           = mlxsw_sp2_nve_vxlan_fini,
+       .fdb_replay     = mlxsw_sp_nve_vxlan_fdb_replay,
+       .fdb_clear_offload = mlxsw_sp_nve_vxlan_clear_offload,
 };
index 6ebf99c..f49a220 100644 (file)
@@ -7293,6 +7293,15 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = {
        .fdb_del                = mlxsw_sp_rif_fid_fdb_del,
 };
 
+static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_vlan_emu_ops = {
+       .type                   = MLXSW_SP_RIF_TYPE_VLAN,
+       .rif_size               = sizeof(struct mlxsw_sp_rif),
+       .configure              = mlxsw_sp_rif_fid_configure,
+       .deconfigure            = mlxsw_sp_rif_fid_deconfigure,
+       .fid_get                = mlxsw_sp_rif_vlan_fid_get,
+       .fdb_del                = mlxsw_sp_rif_vlan_fdb_del,
+};
+
 static struct mlxsw_sp_rif_ipip_lb *
 mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif)
 {
@@ -7361,7 +7370,7 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = {
 
 static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = {
        [MLXSW_SP_RIF_TYPE_SUBPORT]     = &mlxsw_sp_rif_subport_ops,
-       [MLXSW_SP_RIF_TYPE_VLAN]        = &mlxsw_sp_rif_vlan_ops,
+       [MLXSW_SP_RIF_TYPE_VLAN]        = &mlxsw_sp_rif_vlan_emu_ops,
        [MLXSW_SP_RIF_TYPE_FID]         = &mlxsw_sp_rif_fid_ops,
        [MLXSW_SP_RIF_TYPE_IPIP_LB]     = &mlxsw_sp_rif_ipip_lb_ops,
 };
index 50080c6..5ad1fce 100644 (file)
@@ -85,10 +85,8 @@ struct mlxsw_sp_bridge_ops {
                           struct mlxsw_sp_bridge_port *bridge_port,
                           struct mlxsw_sp_port *mlxsw_sp_port);
        int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device,
-                         const struct net_device *vxlan_dev,
+                         const struct net_device *vxlan_dev, u16 vid,
                          struct netlink_ext_ack *extack);
-       void (*vxlan_leave)(struct mlxsw_sp_bridge_device *bridge_device,
-                           const struct net_device *vxlan_dev);
        struct mlxsw_sp_fid *
                (*fid_get)(struct mlxsw_sp_bridge_device *bridge_device,
                           u16 vid);
@@ -1974,8 +1972,6 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
 static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
        .switchdev_port_attr_get        = mlxsw_sp_port_attr_get,
        .switchdev_port_attr_set        = mlxsw_sp_port_attr_set,
-       .switchdev_port_obj_add         = mlxsw_sp_port_obj_add,
-       .switchdev_port_obj_del         = mlxsw_sp_port_obj_del,
 };
 
 static int
@@ -2013,17 +2009,79 @@ mlxsw_sp_bridge_8021q_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
 
 static int
 mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
-                                const struct net_device *vxlan_dev,
+                                const struct net_device *vxlan_dev, u16 vid,
                                 struct netlink_ext_ack *extack)
 {
-       WARN_ON(1);
-       return -EINVAL;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       struct mlxsw_sp_nve_params params = {
+               .type = MLXSW_SP_NVE_TYPE_VXLAN,
+               .vni = vxlan->cfg.vni,
+               .dev = vxlan_dev,
+       };
+       struct mlxsw_sp_fid *fid;
+       int err;
+
+       /* If the VLAN is 0, we need to find the VLAN that is configured as
+        * PVID and egress untagged on the bridge port of the VxLAN device.
+        * It is possible no such VLAN exists
+        */
+       if (!vid) {
+               err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid);
+               if (err || !vid)
+                       return err;
+       }
+
+       /* If no other port is member in the VLAN, then the FID does not exist.
+        * NVE will be enabled on the FID once a port joins the VLAN
+        */
+       fid = mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
+       if (!fid)
+               return 0;
+
+       if (mlxsw_sp_fid_vni_is_set(fid)) {
+               err = -EINVAL;
+               goto err_vni_exists;
+       }
+
+       err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, &params, extack);
+       if (err)
+               goto err_nve_fid_enable;
+
+       /* The tunnel port does not hold a reference on the FID. Only
+        * local ports and the router port
+        */
+       mlxsw_sp_fid_put(fid);
+
+       return 0;
+
+err_nve_fid_enable:
+err_vni_exists:
+       mlxsw_sp_fid_put(fid);
+       return err;
 }
 
-static void
-mlxsw_sp_bridge_8021q_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
-                                 const struct net_device *vxlan_dev)
+static struct net_device *
+mlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid)
 {
+       struct net_device *dev;
+       struct list_head *iter;
+
+       netdev_for_each_lower_dev(br_dev, dev, iter) {
+               u16 pvid;
+               int err;
+
+               if (!netif_is_vxlan(dev))
+                       continue;
+
+               err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
+               if (err || pvid != vid)
+                       continue;
+
+               return dev;
+       }
+
+       return NULL;
 }
 
 static struct mlxsw_sp_fid *
@@ -2031,16 +2089,47 @@ mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
                              u16 vid)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+       struct net_device *vxlan_dev;
+       struct mlxsw_sp_fid *fid;
+       int err;
+
+       fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
+       if (IS_ERR(fid))
+               return fid;
+
+       if (mlxsw_sp_fid_vni_is_set(fid))
+               return fid;
+
+       /* Find the VxLAN device that has the specified VLAN configured as
+        * PVID and egress untagged. There can be at most one such device
+        */
+       vxlan_dev = mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev,
+                                                        vid);
+       if (!vxlan_dev)
+               return fid;
+
+       if (!netif_running(vxlan_dev))
+               return fid;
+
+       err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
+                                              NULL);
+       if (err)
+               goto err_vxlan_join;
+
+       return fid;
 
-       return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid);
+err_vxlan_join:
+       mlxsw_sp_fid_put(fid);
+       return ERR_PTR(err);
 }
 
 static struct mlxsw_sp_fid *
 mlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device,
                                 u16 vid)
 {
-       WARN_ON(1);
-       return NULL;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
+
+       return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid);
 }
 
 static u16
@@ -2054,7 +2143,6 @@ static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = {
        .port_join      = mlxsw_sp_bridge_8021q_port_join,
        .port_leave     = mlxsw_sp_bridge_8021q_port_leave,
        .vxlan_join     = mlxsw_sp_bridge_8021q_vxlan_join,
-       .vxlan_leave    = mlxsw_sp_bridge_8021q_vxlan_leave,
        .fid_get        = mlxsw_sp_bridge_8021q_fid_get,
        .fid_lookup     = mlxsw_sp_bridge_8021q_fid_lookup,
        .fid_vid        = mlxsw_sp_bridge_8021q_fid_vid,
@@ -2123,7 +2211,7 @@ mlxsw_sp_bridge_8021d_port_leave(struct mlxsw_sp_bridge_device *bridge_device,
 
 static int
 mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
-                                const struct net_device *vxlan_dev,
+                                const struct net_device *vxlan_dev, u16 vid,
                                 struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
@@ -2162,26 +2250,6 @@ err_vni_exists:
        return err;
 }
 
-static void
-mlxsw_sp_bridge_8021d_vxlan_leave(struct mlxsw_sp_bridge_device *bridge_device,
-                                 const struct net_device *vxlan_dev)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
-       struct mlxsw_sp_fid *fid;
-
-       fid = mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex);
-       if (WARN_ON(!fid))
-               return;
-
-       /* If the VxLAN device is down, then the FID does not have a VNI */
-       if (!mlxsw_sp_fid_vni_is_set(fid))
-               goto out;
-
-       mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
-out:
-       mlxsw_sp_fid_put(fid);
-}
-
 static struct mlxsw_sp_fid *
 mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
                              u16 vid)
@@ -2205,7 +2273,8 @@ mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device,
        if (!netif_running(vxlan_dev))
                return fid;
 
-       err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, NULL);
+       err = mlxsw_sp_bridge_8021d_vxlan_join(bridge_device, vxlan_dev, 0,
+                                              NULL);
        if (err)
                goto err_vxlan_join;
 
@@ -2240,7 +2309,6 @@ static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = {
        .port_join      = mlxsw_sp_bridge_8021d_port_join,
        .port_leave     = mlxsw_sp_bridge_8021d_port_leave,
        .vxlan_join     = mlxsw_sp_bridge_8021d_vxlan_join,
-       .vxlan_leave    = mlxsw_sp_bridge_8021d_vxlan_leave,
        .fid_get        = mlxsw_sp_bridge_8021d_fid_get,
        .fid_lookup     = mlxsw_sp_bridge_8021d_fid_lookup,
        .fid_vid        = mlxsw_sp_bridge_8021d_fid_vid,
@@ -2295,7 +2363,7 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
 
 int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
                               const struct net_device *br_dev,
-                              const struct net_device *vxlan_dev,
+                              const struct net_device *vxlan_dev, u16 vid,
                               struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_bridge_device *bridge_device;
@@ -2304,20 +2372,88 @@ int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp,
        if (WARN_ON(!bridge_device))
                return -EINVAL;
 
-       return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, extack);
+       return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid,
+                                             extack);
 }
 
 void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
-                                const struct net_device *br_dev,
                                 const struct net_device *vxlan_dev)
 {
-       struct mlxsw_sp_bridge_device *bridge_device;
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       struct mlxsw_sp_fid *fid;
 
-       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
-       if (WARN_ON(!bridge_device))
+       /* If the VxLAN device is down, then the FID does not have a VNI */
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
+       if (!fid)
                return;
 
-       bridge_device->ops->vxlan_leave(bridge_device, vxlan_dev);
+       mlxsw_sp_nve_fid_disable(mlxsw_sp, fid);
+       mlxsw_sp_fid_put(fid);
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
+                                     enum mlxsw_sp_l3proto *proto,
+                                     union mlxsw_sp_l3addr *addr)
+{
+       if (vxlan_addr->sa.sa_family == AF_INET) {
+               addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
+               *proto = MLXSW_SP_L3_PROTO_IPV4;
+       } else {
+               addr->addr6 = vxlan_addr->sin6.sin6_addr;
+               *proto = MLXSW_SP_L3_PROTO_IPV6;
+       }
+}
+
+static void
+mlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto,
+                                     const union mlxsw_sp_l3addr *addr,
+                                     union vxlan_addr *vxlan_addr)
+{
+       switch (proto) {
+       case MLXSW_SP_L3_PROTO_IPV4:
+               vxlan_addr->sa.sa_family = AF_INET;
+               vxlan_addr->sin.sin_addr.s_addr = addr->addr4;
+               break;
+       case MLXSW_SP_L3_PROTO_IPV6:
+               vxlan_addr->sa.sa_family = AF_INET6;
+               vxlan_addr->sin6.sin6_addr = addr->addr6;
+               break;
+       }
+}
+
+static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
+                                             const char *mac,
+                                             enum mlxsw_sp_l3proto proto,
+                                             union mlxsw_sp_l3addr *addr,
+                                             __be32 vni, bool adding)
+{
+       struct switchdev_notifier_vxlan_fdb_info info;
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       enum switchdev_notifier_type type;
+
+       type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
+                       SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
+       mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
+       info.remote_port = vxlan->cfg.dst_port;
+       info.remote_vni = vni;
+       info.remote_ifindex = 0;
+       ether_addr_copy(info.eth_addr, mac);
+       info.vni = vni;
+       info.offloaded = adding;
+       call_switchdev_notifiers(type, dev, &info.info);
+}
+
+static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev,
+                                           const char *mac,
+                                           enum mlxsw_sp_l3proto proto,
+                                           union mlxsw_sp_l3addr *addr,
+                                           __be32 vni,
+                                           bool adding)
+{
+       if (netif_is_vxlan(dev))
+               mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni,
+                                                 adding);
 }
 
 static void
@@ -2428,7 +2564,8 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
 
        bridge_device = bridge_port->bridge_device;
        vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0;
-       lag_vid = mlxsw_sp_port_vlan->vid;
+       lag_vid = mlxsw_sp_fid_lag_vid_valid(mlxsw_sp_port_vlan->fid) ?
+                 mlxsw_sp_port_vlan->vid : 0;
 
 do_fdb_op:
        err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
@@ -2451,6 +2588,122 @@ just_remove:
        goto do_fdb_op;
 }
 
+static int
+__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
+                                           const struct mlxsw_sp_fid *fid,
+                                           bool adding,
+                                           struct net_device **nve_dev,
+                                           u16 *p_vid, __be32 *p_vni)
+{
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct net_device *br_dev, *dev;
+       int nve_ifindex;
+       int err;
+
+       err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_fid_vni(fid, p_vni);
+       if (err)
+               return err;
+
+       dev = __dev_get_by_index(&init_net, nve_ifindex);
+       if (!dev)
+               return -EINVAL;
+       *nve_dev = dev;
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       if (adding && !br_port_flag_is_set(dev, BR_LEARNING))
+               return -EINVAL;
+
+       if (adding && netif_is_vxlan(dev)) {
+               struct vxlan_dev *vxlan = netdev_priv(dev);
+
+               if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+                       return -EINVAL;
+       }
+
+       br_dev = netdev_master_upper_dev_get(dev);
+       if (!br_dev)
+               return -EINVAL;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return -EINVAL;
+
+       *p_vid = bridge_device->ops->fid_vid(bridge_device, fid);
+
+       return 0;
+}
+
+static void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
+                                                     char *sfn_pl,
+                                                     int rec_index,
+                                                     bool adding)
+{
+       enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto;
+       enum switchdev_notifier_type type;
+       struct net_device *nve_dev;
+       union mlxsw_sp_l3addr addr;
+       struct mlxsw_sp_fid *fid;
+       char mac[ETH_ALEN];
+       u16 fid_index, vid;
+       __be32 vni;
+       u32 uip;
+       int err;
+
+       mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index,
+                                      &uip, &sfn_proto);
+
+       fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index);
+       if (!fid)
+               goto err_fid_lookup;
+
+       err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip,
+                                             (enum mlxsw_sp_l3proto) sfn_proto,
+                                             &addr);
+       if (err)
+               goto err_ip_resolve;
+
+       err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding,
+                                                         &nve_dev, &vid, &vni);
+       if (err)
+               goto err_fdb_process;
+
+       err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
+                                            (enum mlxsw_sp_l3proto) sfn_proto,
+                                            &addr, adding, true);
+       if (err)
+               goto err_fdb_op;
+
+       mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac,
+                                       (enum mlxsw_sp_l3proto) sfn_proto,
+                                       &addr, vni, adding);
+
+       type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE :
+                       SWITCHDEV_FDB_DEL_TO_BRIDGE;
+       mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding);
+
+       mlxsw_sp_fid_put(fid);
+
+       return;
+
+err_fdb_op:
+err_fdb_process:
+err_ip_resolve:
+       mlxsw_sp_fid_put(fid);
+err_fid_lookup:
+       /* Remove an FDB entry in case we cannot process it. Otherwise the
+        * device will keep sending the same notification over and over again.
+        */
+       mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index,
+                                      (enum mlxsw_sp_l3proto) sfn_proto, &addr,
+                                      false, true);
+}
+
 static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
                                            char *sfn_pl, int rec_index)
 {
@@ -2471,6 +2724,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
                                                    rec_index, false);
                break;
+       case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL:
+               mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
+                                                         rec_index, true);
+               break;
+       case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL:
+               mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl,
+                                                         rec_index, false);
+               break;
        }
 }
 
@@ -2526,20 +2787,6 @@ struct mlxsw_sp_switchdev_event_work {
 };
 
 static void
-mlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr,
-                                     enum mlxsw_sp_l3proto *proto,
-                                     union mlxsw_sp_l3addr *addr)
-{
-       if (vxlan_addr->sa.sa_family == AF_INET) {
-               addr->addr4 = vxlan_addr->sin.sin_addr.s_addr;
-               *proto = MLXSW_SP_L3_PROTO_IPV4;
-       } else {
-               addr->addr6 = vxlan_addr->sin6.sin6_addr;
-               *proto = MLXSW_SP_L3_PROTO_IPV6;
-       }
-}
-
-static void
 mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp,
                                          struct mlxsw_sp_switchdev_event_work *
                                          switchdev_work,
@@ -2604,7 +2851,8 @@ mlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work *
            switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE)
                return;
 
-       if (!switchdev_work->fdb_info.added_by_user)
+       if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE &&
+           !switchdev_work->fdb_info.added_by_user)
                return;
 
        if (!netif_running(dev))
@@ -2947,10 +3195,270 @@ err_addr_alloc:
        return NOTIFY_BAD;
 }
 
-static struct notifier_block mlxsw_sp_switchdev_notifier = {
+struct notifier_block mlxsw_sp_switchdev_notifier = {
        .notifier_call = mlxsw_sp_switchdev_event,
 };
 
+static int
+mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_bridge_device *bridge_device,
+                                 const struct net_device *vxlan_dev, u16 vid,
+                                 bool flag_untagged, bool flag_pvid,
+                                 struct switchdev_trans *trans)
+{
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       __be32 vni = vxlan->cfg.vni;
+       struct mlxsw_sp_fid *fid;
+       u16 old_vid;
+       int err;
+
+       /* We cannot have the same VLAN as PVID and egress untagged on multiple
+        * VxLAN devices. Note that we get this notification before the VLAN is
+        * actually added to the bridge's database, so it is not possible for
+        * the lookup function to return 'vxlan_dev'
+        */
+       if (flag_untagged && flag_pvid &&
+           mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid))
+               return -EINVAL;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       if (!netif_running(vxlan_dev))
+               return 0;
+
+       /* First case: FID is not associated with this VNI, but the new VLAN
+        * is both PVID and egress untagged. Need to enable NVE on the FID, if
+        * it exists
+        */
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
+       if (!fid) {
+               if (!flag_untagged || !flag_pvid)
+                       return 0;
+               return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device,
+                                                       vxlan_dev, vid, NULL);
+       }
+
+       /* Second case: FID is associated with the VNI and the VLAN associated
+        * with the FID is the same as the notified VLAN. This means the flags
+        * (PVID / egress untagged) were toggled and that NVE should be
+        * disabled on the FID
+        */
+       old_vid = mlxsw_sp_fid_8021q_vid(fid);
+       if (vid == old_vid) {
+               if (WARN_ON(flag_untagged && flag_pvid)) {
+                       mlxsw_sp_fid_put(fid);
+                       return -EINVAL;
+               }
+               mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+               mlxsw_sp_fid_put(fid);
+               return 0;
+       }
+
+       /* Third case: A new VLAN was configured on the VxLAN device, but this
+        * VLAN is not PVID, so there is nothing to do.
+        */
+       if (!flag_pvid) {
+               mlxsw_sp_fid_put(fid);
+               return 0;
+       }
+
+       /* Fourth case: Thew new VLAN is PVID, which means the VLAN currently
+        * mapped to the VNI should be unmapped
+        */
+       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+       mlxsw_sp_fid_put(fid);
+
+       /* Fifth case: The new VLAN is also egress untagged, which means the
+        * VLAN needs to be mapped to the VNI
+        */
+       if (!flag_untagged)
+               return 0;
+
+       err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid,
+                                              NULL);
+       if (err)
+               goto err_vxlan_join;
+
+       return 0;
+
+err_vxlan_join:
+       mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, old_vid,
+                                        NULL);
+       return err;
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_bridge_device *bridge_device,
+                                 const struct net_device *vxlan_dev, u16 vid)
+{
+       struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+       __be32 vni = vxlan->cfg.vni;
+       struct mlxsw_sp_fid *fid;
+
+       if (!netif_running(vxlan_dev))
+               return;
+
+       fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
+       if (!fid)
+               return;
+
+       /* A different VLAN than the one mapped to the VNI is deleted */
+       if (mlxsw_sp_fid_8021q_vid(fid) != vid)
+               goto out;
+
+       mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev);
+
+out:
+       mlxsw_sp_fid_put(fid);
+}
+
+static int
+mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
+                                  struct switchdev_notifier_port_obj_info *
+                                  port_obj_info)
+{
+       struct switchdev_obj_port_vlan *vlan =
+               SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
+       bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       struct switchdev_trans *trans = port_obj_info->trans;
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct mlxsw_sp *mlxsw_sp;
+       struct net_device *br_dev;
+       u16 vid;
+
+       br_dev = netdev_master_upper_dev_get(vxlan_dev);
+       if (!br_dev)
+               return 0;
+
+       mlxsw_sp = mlxsw_sp_lower_get(br_dev);
+       if (!mlxsw_sp)
+               return 0;
+
+       port_obj_info->handled = true;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return -EINVAL;
+
+       if (!bridge_device->vlan_enabled)
+               return 0;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+               int err;
+
+               err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
+                                                       vxlan_dev, vid,
+                                                       flag_untagged,
+                                                       flag_pvid, trans);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void
+mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
+                                  struct switchdev_notifier_port_obj_info *
+                                  port_obj_info)
+{
+       struct switchdev_obj_port_vlan *vlan =
+               SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
+       struct mlxsw_sp_bridge_device *bridge_device;
+       struct mlxsw_sp *mlxsw_sp;
+       struct net_device *br_dev;
+       u16 vid;
+
+       br_dev = netdev_master_upper_dev_get(vxlan_dev);
+       if (!br_dev)
+               return;
+
+       mlxsw_sp = mlxsw_sp_lower_get(br_dev);
+       if (!mlxsw_sp)
+               return;
+
+       port_obj_info->handled = true;
+
+       bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
+       if (!bridge_device)
+               return;
+
+       if (!bridge_device->vlan_enabled)
+               return;
+
+       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
+               mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device,
+                                                 vxlan_dev, vid);
+}
+
+static int
+mlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev,
+                                       struct switchdev_notifier_port_obj_info *
+                                       port_obj_info)
+{
+       int err = 0;
+
+       switch (port_obj_info->obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev,
+                                                        port_obj_info);
+               break;
+       default:
+               break;
+       }
+
+       return err;
+}
+
+static void
+mlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev,
+                                       struct switchdev_notifier_port_obj_info *
+                                       port_obj_info)
+{
+       switch (port_obj_info->obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_VLAN:
+               mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info);
+               break;
+       default:
+               break;
+       }
+}
+
+static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused,
+                                            unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       int err = 0;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               if (netif_is_vxlan(dev))
+                       err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr);
+               else
+                       err = switchdev_handle_port_obj_add(dev, ptr,
+                                                       mlxsw_sp_port_dev_check,
+                                                       mlxsw_sp_port_obj_add);
+               return notifier_from_errno(err);
+       case SWITCHDEV_PORT_OBJ_DEL:
+               if (netif_is_vxlan(dev))
+                       mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr);
+               else
+                       err = switchdev_handle_port_obj_del(dev, ptr,
+                                                       mlxsw_sp_port_dev_check,
+                                                       mlxsw_sp_port_obj_del);
+               return notifier_from_errno(err);
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mlxsw_sp_switchdev_blocking_notifier = {
+       .notifier_call = mlxsw_sp_switchdev_blocking_event,
+};
+
 u8
 mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
 {
@@ -2960,6 +3468,7 @@ mlxsw_sp_bridge_port_stp_state(struct mlxsw_sp_bridge_port *bridge_port)
 static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
 {
        struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
+       struct notifier_block *nb;
        int err;
 
        err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
@@ -2974,17 +3483,33 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
                return err;
        }
 
+       nb = &mlxsw_sp_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err) {
+               dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n");
+               goto err_register_switchdev_blocking_notifier;
+       }
+
        INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
        bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
        mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
        return 0;
+
+err_register_switchdev_blocking_notifier:
+       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
+       return err;
 }
 
 static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
 {
+       struct notifier_block *nb;
+
        cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
-       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
 
+       nb = &mlxsw_sp_switchdev_blocking_notifier;
+       unregister_switchdev_blocking_notifier(nb);
+
+       unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
 }
 
 int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
index 3238b9e..7f8da88 100644 (file)
@@ -1337,8 +1337,6 @@ static int ocelot_port_obj_del(struct net_device *dev,
 static const struct switchdev_ops ocelot_port_switchdev_ops = {
        .switchdev_port_attr_get        = ocelot_port_attr_get,
        .switchdev_port_attr_set        = ocelot_port_attr_set,
-       .switchdev_port_obj_add         = ocelot_port_obj_add,
-       .switchdev_port_obj_del         = ocelot_port_obj_del,
 };
 
 static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port,
@@ -1595,6 +1593,34 @@ struct notifier_block ocelot_netdevice_nb __read_mostly = {
 };
 EXPORT_SYMBOL(ocelot_netdevice_nb);
 
+static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
+                                          unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       int err;
+
+       switch (event) {
+               /* Blocking events. */
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = switchdev_handle_port_obj_add(dev, ptr,
+                                                   ocelot_netdevice_dev_check,
+                                                   ocelot_port_obj_add);
+               return notifier_from_errno(err);
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = switchdev_handle_port_obj_del(dev, ptr,
+                                                   ocelot_netdevice_dev_check,
+                                                   ocelot_port_obj_del);
+               return notifier_from_errno(err);
+       }
+
+       return NOTIFY_DONE;
+}
+
+struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
+       .notifier_call = ocelot_switchdev_blocking_event,
+};
+EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
+
 int ocelot_probe_port(struct ocelot *ocelot, u8 port,
                      void __iomem *regs,
                      struct phy_device *phy)
index 62c7c8e..086775f 100644 (file)
@@ -499,5 +499,6 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
                      struct phy_device *phy);
 
 extern struct notifier_block ocelot_netdevice_nb;
+extern struct notifier_block ocelot_switchdev_blocking_nb;
 
 #endif
index 4c23d18..ca3ea2f 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/of_platform.h>
 #include <linux/mfd/syscon.h>
 #include <linux/skbuff.h>
+#include <net/switchdev.h>
 
 #include "ocelot.h"
 
@@ -328,6 +329,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        }
 
        register_netdevice_notifier(&ocelot_netdevice_nb);
+       register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
 
        dev_info(&pdev->dev, "Ocelot switch probed\n");
 
@@ -342,6 +344,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
        struct ocelot *ocelot = platform_get_drvdata(pdev);
 
        ocelot_deinit(ocelot);
+       unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
        unregister_netdevice_notifier(&ocelot_netdevice_nb);
 
        return 0;
index c26e0f7..7df2056 100644 (file)
@@ -26,7 +26,7 @@ config S2IO
          on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/s2io.txt>.
+         <file:Documentation/networking/device_drivers/neterion/s2io.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called s2io.
@@ -41,7 +41,7 @@ config VXGE
          labeled as either one, depending on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/vxge.txt>.
+         <file:Documentation/networking/device_drivers/neterion/vxge.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called vxge.
index f7a0d1d..59e77e3 100644 (file)
@@ -1695,17 +1695,10 @@ exit:
  */
 void vxge_hw_fifo_txdl_free(struct __vxge_hw_fifo *fifo, void *txdlh)
 {
-       struct __vxge_hw_fifo_txdl_priv *txdl_priv;
-       u32 max_frags;
        struct __vxge_hw_channel *channel;
 
        channel = &fifo->channel;
 
-       txdl_priv = __vxge_hw_fifo_txdl_priv(fifo,
-                       (struct vxge_hw_fifo_txd *)txdlh);
-
-       max_frags = fifo->config->max_frags;
-
        vxge_hw_channel_dtr_free(channel, txdlh);
 }
 
index 4afb103..47c708f 100644 (file)
@@ -56,7 +56,9 @@ endif
 
 ifeq ($(CONFIG_NFP_APP_ABM_NIC),y)
 nfp-objs += \
+           abm/cls.o \
            abm/ctrl.o \
+           abm/qdisc.o \
            abm/main.o
 endif
 
diff --git a/drivers/net/ethernet/netronome/nfp/abm/cls.c b/drivers/net/ethernet/netronome/nfp/abm/cls.c
new file mode 100644 (file)
index 0000000..9852080
--- /dev/null
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+
+#include <linux/bitfield.h>
+#include <net/pkt_cls.h>
+
+#include "../nfpcore/nfp_cpp.h"
+#include "../nfp_app.h"
+#include "../nfp_net_repr.h"
+#include "main.h"
+
+struct nfp_abm_u32_match {
+       u32 handle;
+       u32 band;
+       u8 mask;
+       u8 val;
+       struct list_head list;
+};
+
+static bool
+nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode,
+                       __be16 proto, struct netlink_ext_ack *extack)
+{
+       struct tc_u32_key *k;
+       unsigned int tos_off;
+
+       if (knode->exts && tcf_exts_has_actions(knode->exts)) {
+               NL_SET_ERR_MSG_MOD(extack, "action offload not supported");
+               return false;
+       }
+       if (knode->link_handle) {
+               NL_SET_ERR_MSG_MOD(extack, "linking not supported");
+               return false;
+       }
+       if (knode->sel->flags != TC_U32_TERMINAL) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "flags must be equal to TC_U32_TERMINAL");
+               return false;
+       }
+       if (knode->sel->off || knode->sel->offshift || knode->sel->offmask ||
+           knode->sel->offoff || knode->fshift) {
+               NL_SET_ERR_MSG_MOD(extack, "variable offseting not supported");
+               return false;
+       }
+       if (knode->sel->hoff || knode->sel->hmask) {
+               NL_SET_ERR_MSG_MOD(extack, "hashing not supported");
+               return false;
+       }
+       if (knode->val || knode->mask) {
+               NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported");
+               return false;
+       }
+       if (knode->res && knode->res->class) {
+               NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported");
+               return false;
+       }
+       if (knode->res && knode->res->classid >= abm->num_bands) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "classid higher than number of bands");
+               return false;
+       }
+       if (knode->sel->nkeys != 1) {
+               NL_SET_ERR_MSG_MOD(extack, "exactly one key required");
+               return false;
+       }
+
+       switch (proto) {
+       case htons(ETH_P_IP):
+               tos_off = 16;
+               break;
+       case htons(ETH_P_IPV6):
+               tos_off = 20;
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol");
+               return false;
+       }
+
+       k = &knode->sel->keys[0];
+       if (k->offmask) {
+               NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offseting not supported");
+               return false;
+       }
+       if (k->off) {
+               NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched");
+               return false;
+       }
+       if (k->val & ~k->mask) {
+               NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key");
+               return false;
+       }
+       if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) {
+               NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used");
+               nfp_err(abm->app->cpp,
+                       "u32 offload: requested mask %x FW can support only %x\n",
+                       be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask);
+               return false;
+       }
+
+       return true;
+}
+
+/* This filter list -> map conversion is O(n * m), we expect single digit or
+ * low double digit number of prios and likewise for the filters.  Also u32
+ * doesn't report stats, so it's really only setup time cost.
+ */
+static unsigned int
+nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio)
+{
+       struct nfp_abm_u32_match *iter;
+
+       list_for_each_entry(iter, &alink->dscp_map, list)
+               if ((prio & iter->mask) == iter->val)
+                       return iter->band;
+
+       return alink->def_band;
+}
+
+static int nfp_abm_update_band_map(struct nfp_abm_link *alink)
+{
+       unsigned int i, bits_per_prio, prios_per_word, base_shift;
+       struct nfp_abm *abm = alink->abm;
+       u32 field_mask;
+
+       alink->has_prio = !list_empty(&alink->dscp_map);
+
+       bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands));
+       field_mask = (1 << bits_per_prio) - 1;
+       prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio;
+
+       /* FW mask applies from top bits */
+       base_shift = 8 - order_base_2(abm->num_prios);
+
+       for (i = 0; i < abm->num_prios; i++) {
+               unsigned int offset;
+               u32 *word;
+               u8 band;
+
+               word = &alink->prio_map[i / prios_per_word];
+               offset = (i % prios_per_word) * bits_per_prio;
+
+               band = nfp_abm_find_band_for_prio(alink, i << base_shift);
+
+               *word &= ~(field_mask << offset);
+               *word |= band << offset;
+       }
+
+       /* Qdisc offload status may change if has_prio changed */
+       nfp_abm_qdisc_offload_update(alink);
+
+       return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
+}
+
+static void
+nfp_abm_u32_knode_delete(struct nfp_abm_link *alink,
+                        struct tc_cls_u32_knode *knode)
+{
+       struct nfp_abm_u32_match *iter;
+
+       list_for_each_entry(iter, &alink->dscp_map, list)
+               if (iter->handle == knode->handle) {
+                       list_del(&iter->list);
+                       kfree(iter);
+                       nfp_abm_update_band_map(alink);
+                       return;
+               }
+}
+
+static int
+nfp_abm_u32_knode_replace(struct nfp_abm_link *alink,
+                         struct tc_cls_u32_knode *knode,
+                         __be16 proto, struct netlink_ext_ack *extack)
+{
+       struct nfp_abm_u32_match *match = NULL, *iter;
+       unsigned int tos_off;
+       u8 mask, val;
+       int err;
+
+       if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack))
+               goto err_delete;
+
+       tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
+
+       /* Extract the DSCP Class Selector bits */
+       val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
+       mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
+
+       /* Check if there is no conflicting mapping and find match by handle */
+       list_for_each_entry(iter, &alink->dscp_map, list) {
+               u32 cmask;
+
+               if (iter->handle == knode->handle) {
+                       match = iter;
+                       continue;
+               }
+
+               cmask = iter->mask & mask;
+               if ((iter->val & cmask) == (val & cmask) &&
+                   iter->band != knode->res->classid) {
+                       NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
+                       goto err_delete;
+               }
+       }
+
+       if (!match) {
+               match = kzalloc(sizeof(*match), GFP_KERNEL);
+               if (!match)
+                       return -ENOMEM;
+               list_add(&match->list, &alink->dscp_map);
+       }
+       match->handle = knode->handle;
+       match->band = knode->res->classid;
+       match->mask = mask;
+       match->val = val;
+
+       err = nfp_abm_update_band_map(alink);
+       if (err)
+               goto err_delete;
+
+       return 0;
+
+err_delete:
+       nfp_abm_u32_knode_delete(alink, knode);
+       return -EOPNOTSUPP;
+}
+
+static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
+                                    void *type_data, void *cb_priv)
+{
+       struct tc_cls_u32_offload *cls_u32 = type_data;
+       struct nfp_repr *repr = cb_priv;
+       struct nfp_abm_link *alink;
+
+       alink = repr->app_priv;
+
+       if (type != TC_SETUP_CLSU32) {
+               NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
+                                  "only offload of u32 classifier supported");
+               return -EOPNOTSUPP;
+       }
+       if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
+               return -EOPNOTSUPP;
+
+       if (cls_u32->common.protocol != htons(ETH_P_IP) &&
+           cls_u32->common.protocol != htons(ETH_P_IPV6)) {
+               NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
+                                  "only IP and IPv6 supported as filter protocol");
+               return -EOPNOTSUPP;
+       }
+
+       switch (cls_u32->command) {
+       case TC_CLSU32_NEW_KNODE:
+       case TC_CLSU32_REPLACE_KNODE:
+               return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
+                                                cls_u32->common.protocol,
+                                                cls_u32->common.extack);
+       case TC_CLSU32_DELETE_KNODE:
+               nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
+                           struct tc_block_offload *f)
+{
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case TC_BLOCK_BIND:
+               return tcf_block_cb_register(f->block,
+                                            nfp_abm_setup_tc_block_cb,
+                                            repr, repr, f->extack);
+       case TC_BLOCK_UNBIND:
+               tcf_block_cb_unregister(f->block, nfp_abm_setup_tc_block_cb,
+                                       repr);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
index 3c661f4..ad6c2a6 100644 (file)
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 
+#include <linux/bitops.h>
 #include <linux/kernel.h>
+#include <linux/log2.h>
 
 #include "../nfpcore/nfp_cpp.h"
 #include "../nfpcore/nfp_nffw.h"
 #include "../nfp_net.h"
 #include "main.h"
 
-#define NFP_QLVL_SYM_NAME      "_abi_nfd_out_q_lvls_%u"
+#define NFP_NUM_PRIOS_SYM_NAME "_abi_pci_dscp_num_prio_%u"
+#define NFP_NUM_BANDS_SYM_NAME "_abi_pci_dscp_num_band_%u"
+#define NFP_ACT_MASK_SYM_NAME  "_abi_nfd_out_q_actions_%u"
+
+#define NFP_QLVL_SYM_NAME      "_abi_nfd_out_q_lvls_%u%s"
 #define NFP_QLVL_STRIDE                16
 #define NFP_QLVL_BLOG_BYTES    0
 #define NFP_QLVL_BLOG_PKTS     4
 #define NFP_QLVL_THRS          8
+#define NFP_QLVL_ACT           12
 
-#define NFP_QMSTAT_SYM_NAME    "_abi_nfdqm%u_stats"
+#define NFP_QMSTAT_SYM_NAME    "_abi_nfdqm%u_stats%s"
 #define NFP_QMSTAT_STRIDE      32
 #define NFP_QMSTAT_NON_STO     0
 #define NFP_QMSTAT_STO         8
 #define NFP_QMSTAT_DROP                16
 #define NFP_QMSTAT_ECN         24
 
+#define NFP_Q_STAT_SYM_NAME    "_abi_nfd_rxq_stats%u%s"
+#define NFP_Q_STAT_STRIDE      16
+#define NFP_Q_STAT_PKTS                0
+#define NFP_Q_STAT_BYTES       8
+
+#define NFP_NET_ABM_MBOX_CMD           NFP_NET_CFG_MBOX_SIMPLE_CMD
+#define NFP_NET_ABM_MBOX_RET           NFP_NET_CFG_MBOX_SIMPLE_RET
+#define NFP_NET_ABM_MBOX_DATALEN       NFP_NET_CFG_MBOX_SIMPLE_VAL
+#define NFP_NET_ABM_MBOX_RESERVED      (NFP_NET_CFG_MBOX_SIMPLE_VAL + 4)
+#define NFP_NET_ABM_MBOX_DATA          (NFP_NET_CFG_MBOX_SIMPLE_VAL + 8)
+
 static int
 nfp_abm_ctrl_stat(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
-                 unsigned int stride, unsigned int offset, unsigned int i,
-                 bool is_u64, u64 *res)
+                 unsigned int stride, unsigned int offset, unsigned int band,
+                 unsigned int queue, bool is_u64, u64 *res)
 {
        struct nfp_cpp *cpp = alink->abm->app->cpp;
        u64 val, sym_offset;
+       unsigned int qid;
        u32 val32;
        int err;
 
-       sym_offset = (alink->queue_base + i) * stride + offset;
+       qid = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       sym_offset = qid * stride + offset;
        if (is_u64)
                err = __nfp_rtsym_readq(cpp, sym, 3, 0, sym_offset, &val);
        else
                err = __nfp_rtsym_readl(cpp, sym, 3, 0, sym_offset, &val32);
        if (err) {
-               nfp_err(cpp,
-                       "RED offload reading stat failed on vNIC %d queue %d\n",
-                       alink->id, i);
+               nfp_err(cpp, "RED offload reading stat failed on vNIC %d band %d queue %d (+ %d)\n",
+                       alink->id, band, queue, alink->queue_base);
                return err;
        }
 
@@ -50,175 +70,179 @@ nfp_abm_ctrl_stat(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
        return 0;
 }
 
-static int
-nfp_abm_ctrl_stat_all(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
-                     unsigned int stride, unsigned int offset, bool is_u64,
-                     u64 *res)
+int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val)
 {
-       u64 val, sum = 0;
-       unsigned int i;
+       struct nfp_cpp *cpp = abm->app->cpp;
+       u64 sym_offset;
        int err;
 
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               err = nfp_abm_ctrl_stat(alink, sym, stride, offset, i,
-                                       is_u64, &val);
-               if (err)
-                       return err;
-               sum += val;
+       __clear_bit(id, abm->threshold_undef);
+       if (abm->thresholds[id] == val)
+               return 0;
+
+       sym_offset = id * NFP_QLVL_STRIDE + NFP_QLVL_THRS;
+       err = __nfp_rtsym_writel(cpp, abm->q_lvls, 4, 0, sym_offset, val);
+       if (err) {
+               nfp_err(cpp,
+                       "RED offload setting level failed on subqueue %d\n",
+                       id);
+               return err;
        }
 
-       *res = sum;
+       abm->thresholds[id] = val;
        return 0;
 }
 
-int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i, u32 val)
+int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, u32 val)
 {
-       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       unsigned int threshold;
+
+       threshold = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
+
+       return __nfp_abm_ctrl_set_q_lvl(alink->abm, threshold, val);
+}
+
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act)
+{
+       struct nfp_cpp *cpp = abm->app->cpp;
        u64 sym_offset;
        int err;
 
-       sym_offset = (alink->queue_base + i) * NFP_QLVL_STRIDE + NFP_QLVL_THRS;
-       err = __nfp_rtsym_writel(cpp, alink->abm->q_lvls, 4, 0,
-                                sym_offset, val);
+       if (abm->actions[id] == act)
+               return 0;
+
+       sym_offset = id * NFP_QLVL_STRIDE + NFP_QLVL_ACT;
+       err = __nfp_rtsym_writel(cpp, abm->q_lvls, 4, 0, sym_offset, act);
        if (err) {
-               nfp_err(cpp, "RED offload setting level failed on vNIC %d queue %d\n",
-                       alink->id, i);
+               nfp_err(cpp,
+                       "RED offload setting action failed on subqueue %d\n",
+                       id);
                return err;
        }
 
+       abm->actions[id] = act;
        return 0;
 }
 
-int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val)
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act)
 {
-       int i, err;
+       unsigned int qid;
+
+       qid = band * NFP_NET_MAX_RX_RINGS + alink->queue_base + queue;
 
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               err = nfp_abm_ctrl_set_q_lvl(alink, i, val);
-               if (err)
-                       return err;
+       return __nfp_abm_ctrl_set_q_act(alink->abm, qid, act);
+}
+
+u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int queue)
+{
+       unsigned int band;
+       u64 val, sum = 0;
+
+       for (band = 0; band < alink->abm->num_bands; band++) {
+               if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
+                                     NFP_QMSTAT_STRIDE, NFP_QMSTAT_NON_STO,
+                                     band, queue, true, &val))
+                       return 0;
+               sum += val;
        }
 
-       return 0;
+       return sum;
 }
 
-u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i)
+u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int queue)
 {
-       u64 val;
+       unsigned int band;
+       u64 val, sum = 0;
 
-       if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
-                             NFP_QMSTAT_NON_STO, i, true, &val))
-               return 0;
-       return val;
+       for (band = 0; band < alink->abm->num_bands; band++) {
+               if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
+                                     NFP_QMSTAT_STRIDE, NFP_QMSTAT_STO,
+                                     band, queue, true, &val))
+                       return 0;
+               sum += val;
+       }
+
+       return sum;
 }
 
-u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i)
+static int
+nfp_abm_ctrl_stat_basic(struct nfp_abm_link *alink, unsigned int band,
+                       unsigned int queue, unsigned int off, u64 *val)
 {
-       u64 val;
+       if (!nfp_abm_has_prio(alink->abm)) {
+               if (!band) {
+                       unsigned int id = alink->queue_base + queue;
+
+                       *val = nn_readq(alink->vnic,
+                                       NFP_NET_CFG_RXR_STATS(id) + off);
+               } else {
+                       *val = 0;
+               }
 
-       if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
-                             NFP_QMSTAT_STO, i, true, &val))
                return 0;
-       return val;
+       } else {
+               return nfp_abm_ctrl_stat(alink, alink->abm->q_stats,
+                                        NFP_Q_STAT_STRIDE, off, band, queue,
+                                        true, val);
+       }
 }
 
-int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
-                             struct nfp_alink_stats *stats)
+int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int band,
+                             unsigned int queue, struct nfp_alink_stats *stats)
 {
        int err;
 
-       stats->tx_pkts = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
-       stats->tx_bytes = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
+       err = nfp_abm_ctrl_stat_basic(alink, band, queue, NFP_Q_STAT_PKTS,
+                                     &stats->tx_pkts);
+       if (err)
+               return err;
 
-       err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
-                               NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
-                               i, false, &stats->backlog_bytes);
+       err = nfp_abm_ctrl_stat_basic(alink, band, queue, NFP_Q_STAT_BYTES,
+                                     &stats->tx_bytes);
+       if (err)
+               return err;
+
+       err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls, NFP_QLVL_STRIDE,
+                               NFP_QLVL_BLOG_BYTES, band, queue, false,
+                               &stats->backlog_bytes);
        if (err)
                return err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
                                NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
-                               i, false, &stats->backlog_pkts);
+                               band, queue, false, &stats->backlog_pkts);
        if (err)
                return err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                               i, true, &stats->drops);
+                               band, queue, true, &stats->drops);
        if (err)
                return err;
 
        return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                 NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                i, true, &stats->overlimits);
-}
-
-int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
-                           struct nfp_alink_stats *stats)
-{
-       u64 pkts = 0, bytes = 0;
-       int i, err;
-
-       for (i = 0; i < alink->vnic->max_rx_rings; i++) {
-               pkts += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
-               bytes += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
-       }
-       stats->tx_pkts = pkts;
-       stats->tx_bytes = bytes;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
-                                   NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
-                                   false, &stats->backlog_bytes);
-       if (err)
-               return err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
-                                   NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
-                                   false, &stats->backlog_pkts);
-       if (err)
-               return err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                   NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                                   true, &stats->drops);
-       if (err)
-               return err;
-
-       return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                    NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                    true, &stats->overlimits);
+                                band, queue, true, &stats->overlimits);
 }
 
-int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
+int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink,
+                              unsigned int band, unsigned int queue,
                               struct nfp_alink_xstats *xstats)
 {
        int err;
 
        err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                               i, true, &xstats->pdrop);
+                               band, queue, true, &xstats->pdrop);
        if (err)
                return err;
 
        return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
                                 NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                i, true, &xstats->ecn_marked);
-}
-
-int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
-                            struct nfp_alink_xstats *xstats)
-{
-       int err;
-
-       err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                   NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
-                                   true, &xstats->pdrop);
-       if (err)
-               return err;
-
-       return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
-                                    NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
-                                    true, &xstats->ecn_marked);
+                                band, queue, true, &xstats->ecn_marked);
 }
 
 int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm)
@@ -233,10 +257,64 @@ int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm)
                            NULL, 0, NULL, 0);
 }
 
-void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink)
+int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed)
+{
+       struct nfp_net *nn = alink->vnic;
+       unsigned int i;
+       int err;
+
+       /* Write data_len and wipe reserved */
+       nn_writeq(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATALEN,
+                 alink->abm->prio_map_len);
+
+       for (i = 0; i < alink->abm->prio_map_len; i += sizeof(u32))
+               nn_writel(nn, nn->tlv_caps.mbox_off + NFP_NET_ABM_MBOX_DATA + i,
+                         packed[i / sizeof(u32)]);
+
+       err = nfp_net_reconfig_mbox(nn,
+                                   NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET);
+       if (err)
+               nfp_err(alink->abm->app->cpp,
+                       "setting DSCP -> VQ map failed with error %d\n", err);
+       return err;
+}
+
+static int nfp_abm_ctrl_prio_check_params(struct nfp_abm_link *alink)
+{
+       struct nfp_abm *abm = alink->abm;
+       struct nfp_net *nn = alink->vnic;
+       unsigned int min_mbox_sz;
+
+       if (!nfp_abm_has_prio(alink->abm))
+               return 0;
+
+       min_mbox_sz = NFP_NET_ABM_MBOX_DATA + alink->abm->prio_map_len;
+       if (nn->tlv_caps.mbox_len < min_mbox_sz) {
+               nfp_err(abm->app->pf->cpp, "vNIC mailbox too small for prio offload: %u, need: %u\n",
+                       nn->tlv_caps.mbox_len,  min_mbox_sz);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int nfp_abm_ctrl_read_params(struct nfp_abm_link *alink)
 {
        alink->queue_base = nn_readl(alink->vnic, NFP_NET_CFG_START_RXQ);
        alink->queue_base /= alink->vnic->stride_rx;
+
+       return nfp_abm_ctrl_prio_check_params(alink);
+}
+
+static unsigned int nfp_abm_ctrl_prio_map_size(struct nfp_abm *abm)
+{
+       unsigned int size;
+
+       size = roundup_pow_of_two(order_base_2(abm->num_bands));
+       size = DIV_ROUND_UP(size * abm->num_prios, BITS_PER_BYTE);
+       size = round_up(size, sizeof(u32));
+
+       return size;
 }
 
 static const struct nfp_rtsym *
@@ -260,33 +338,77 @@ nfp_abm_ctrl_find_rtsym(struct nfp_pf *pf, const char *name, unsigned int size)
 }
 
 static const struct nfp_rtsym *
-nfp_abm_ctrl_find_q_rtsym(struct nfp_pf *pf, const char *name,
-                         unsigned int size)
+nfp_abm_ctrl_find_q_rtsym(struct nfp_abm *abm, const char *name_fmt,
+                         size_t size)
 {
-       return nfp_abm_ctrl_find_rtsym(pf, name, size * NFP_NET_MAX_RX_RINGS);
+       char pf_symbol[64];
+
+       size = array3_size(size, abm->num_bands, NFP_NET_MAX_RX_RINGS);
+       snprintf(pf_symbol, sizeof(pf_symbol), name_fmt,
+                abm->pf_id, nfp_abm_has_prio(abm) ? "_per_band" : "");
+
+       return nfp_abm_ctrl_find_rtsym(abm->app->pf, pf_symbol, size);
 }
 
 int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm)
 {
        struct nfp_pf *pf = abm->app->pf;
        const struct nfp_rtsym *sym;
-       unsigned int pf_id;
-       char pf_symbol[64];
-
-       pf_id = nfp_cppcore_pcie_unit(pf->cpp);
-       abm->pf_id = pf_id;
+       int res;
+
+       abm->pf_id = nfp_cppcore_pcie_unit(pf->cpp);
+
+       /* Read count of prios and prio bands */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_NUM_BANDS_SYM_NAME, 1);
+       if (res < 0)
+               return res;
+       abm->num_bands = res;
+
+       res = nfp_pf_rtsym_read_optional(pf, NFP_NUM_PRIOS_SYM_NAME, 1);
+       if (res < 0)
+               return res;
+       abm->num_prios = res;
+
+       /* Read available actions */
+       res = nfp_pf_rtsym_read_optional(pf, NFP_ACT_MASK_SYM_NAME,
+                                        BIT(NFP_ABM_ACT_MARK_DROP));
+       if (res < 0)
+               return res;
+       abm->action_mask = res;
+
+       abm->prio_map_len = nfp_abm_ctrl_prio_map_size(abm);
+       abm->dscp_mask = GENMASK(7, 8 - order_base_2(abm->num_prios));
+
+       /* Check values are sane, U16_MAX is arbitrarily chosen as max */
+       if (!is_power_of_2(abm->num_bands) || !is_power_of_2(abm->num_prios) ||
+           abm->num_bands > U16_MAX || abm->num_prios > U16_MAX ||
+           (abm->num_bands == 1) != (abm->num_prios == 1)) {
+               nfp_err(pf->cpp,
+                       "invalid priomap description num bands: %u and num prios: %u\n",
+                       abm->num_bands, abm->num_prios);
+               return -EINVAL;
+       }
 
-       snprintf(pf_symbol, sizeof(pf_symbol), NFP_QLVL_SYM_NAME, pf_id);
-       sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QLVL_STRIDE);
+       /* Find level and stat symbols */
+       sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_QLVL_SYM_NAME,
+                                       NFP_QLVL_STRIDE);
        if (IS_ERR(sym))
                return PTR_ERR(sym);
        abm->q_lvls = sym;
 
-       snprintf(pf_symbol, sizeof(pf_symbol), NFP_QMSTAT_SYM_NAME, pf_id);
-       sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QMSTAT_STRIDE);
+       sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_QMSTAT_SYM_NAME,
+                                       NFP_QMSTAT_STRIDE);
        if (IS_ERR(sym))
                return PTR_ERR(sym);
        abm->qm_stats = sym;
 
+       if (nfp_abm_has_prio(abm)) {
+               sym = nfp_abm_ctrl_find_q_rtsym(abm, NFP_Q_STAT_SYM_NAME,
+                                               NFP_Q_STAT_STRIDE);
+               if (IS_ERR(sym))
+                       return PTR_ERR(sym);
+               abm->q_stats = sym;
+       }
+
        return 0;
 }
index c0830c0..80e7944 100644 (file)
@@ -2,14 +2,13 @@
 /* Copyright (C) 2018 Netronome Systems, Inc. */
 
 #include <linux/bitfield.h>
+#include <linux/bitmap.h>
 #include <linux/etherdevice.h>
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/rcupdate.h>
+#include <linux/rtnetlink.h>
 #include <linux/slab.h>
-#include <net/pkt_cls.h>
-#include <net/pkt_sched.h>
-#include <net/red.h>
 
 #include "../nfpcore/nfp.h"
 #include "../nfpcore/nfp_cpp.h"
@@ -28,269 +27,6 @@ static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id)
 }
 
 static int
-__nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
-                    u32 handle, unsigned int qs, u32 init_val)
-{
-       struct nfp_port *port = nfp_port_from_netdev(netdev);
-       int ret;
-
-       ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val);
-       memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs);
-
-       alink->parent = handle;
-       alink->num_qdiscs = qs;
-       port->tc_offload_cnt = qs;
-
-       return ret;
-}
-
-static void
-nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
-                  u32 handle, unsigned int qs)
-{
-       __nfp_abm_reset_root(netdev, alink, handle, qs, ~0);
-}
-
-static int
-nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
-{
-       unsigned int i = TC_H_MIN(opt->parent) - 1;
-
-       if (opt->parent == TC_H_ROOT)
-               i = 0;
-       else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent))
-               i = TC_H_MIN(opt->parent) - 1;
-       else
-               return -EOPNOTSUPP;
-
-       if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle)
-               return -EOPNOTSUPP;
-
-       return i;
-}
-
-static void
-nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
-                   u32 handle)
-{
-       unsigned int i;
-
-       for (i = 0; i < alink->num_qdiscs; i++)
-               if (handle == alink->qdiscs[i].handle)
-                       break;
-       if (i == alink->num_qdiscs)
-               return;
-
-       if (alink->parent == TC_H_ROOT) {
-               nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
-       } else {
-               nfp_abm_ctrl_set_q_lvl(alink, i, ~0);
-               memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs));
-       }
-}
-
-static int
-nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
-                   struct tc_red_qopt_offload *opt)
-{
-       bool existing;
-       int i, err;
-
-       i = nfp_abm_red_find(alink, opt);
-       existing = i >= 0;
-
-       if (opt->set.min != opt->set.max || !opt->set.is_ecn) {
-               nfp_warn(alink->abm->app->cpp,
-                        "RED offload failed - unsupported parameters\n");
-               err = -EINVAL;
-               goto err_destroy;
-       }
-
-       if (existing) {
-               if (alink->parent == TC_H_ROOT)
-                       err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
-               else
-                       err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
-               if (err)
-                       goto err_destroy;
-               return 0;
-       }
-
-       if (opt->parent == TC_H_ROOT) {
-               i = 0;
-               err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1,
-                                          opt->set.min);
-       } else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) {
-               i = TC_H_MIN(opt->parent) - 1;
-               err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
-       } else {
-               return -EINVAL;
-       }
-       /* Set the handle to try full clean up, in case IO failed */
-       alink->qdiscs[i].handle = opt->handle;
-       if (err)
-               goto err_destroy;
-
-       if (opt->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats);
-       else
-               err = nfp_abm_ctrl_read_q_stats(alink, i,
-                                               &alink->qdiscs[i].stats);
-       if (err)
-               goto err_destroy;
-
-       if (opt->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_xstats(alink,
-                                              &alink->qdiscs[i].xstats);
-       else
-               err = nfp_abm_ctrl_read_q_xstats(alink, i,
-                                                &alink->qdiscs[i].xstats);
-       if (err)
-               goto err_destroy;
-
-       alink->qdiscs[i].stats.backlog_pkts = 0;
-       alink->qdiscs[i].stats.backlog_bytes = 0;
-
-       return 0;
-err_destroy:
-       /* If the qdisc keeps on living, but we can't offload undo changes */
-       if (existing) {
-               opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts;
-               opt->set.qstats->backlog -=
-                       alink->qdiscs[i].stats.backlog_bytes;
-       }
-       nfp_abm_red_destroy(netdev, alink, opt->handle);
-
-       return err;
-}
-
-static void
-nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
-                    struct tc_qopt_offload_stats *stats)
-{
-       _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
-                      new->tx_pkts - old->tx_pkts);
-       stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
-       stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
-       stats->qstats->overlimits += new->overlimits - old->overlimits;
-       stats->qstats->drops += new->drops - old->drops;
-}
-
-static int
-nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
-{
-       struct nfp_alink_stats *prev_stats;
-       struct nfp_alink_stats stats;
-       int i, err;
-
-       i = nfp_abm_red_find(alink, opt);
-       if (i < 0)
-               return i;
-       prev_stats = &alink->qdiscs[i].stats;
-
-       if (alink->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_stats(alink, &stats);
-       else
-               err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
-       if (err)
-               return err;
-
-       nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
-
-       *prev_stats = stats;
-
-       return 0;
-}
-
-static int
-nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
-{
-       struct nfp_alink_xstats *prev_xstats;
-       struct nfp_alink_xstats xstats;
-       int i, err;
-
-       i = nfp_abm_red_find(alink, opt);
-       if (i < 0)
-               return i;
-       prev_xstats = &alink->qdiscs[i].xstats;
-
-       if (alink->parent == TC_H_ROOT)
-               err = nfp_abm_ctrl_read_xstats(alink, &xstats);
-       else
-               err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats);
-       if (err)
-               return err;
-
-       opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
-       opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
-
-       *prev_xstats = xstats;
-
-       return 0;
-}
-
-static int
-nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
-                    struct tc_red_qopt_offload *opt)
-{
-       switch (opt->command) {
-       case TC_RED_REPLACE:
-               return nfp_abm_red_replace(netdev, alink, opt);
-       case TC_RED_DESTROY:
-               nfp_abm_red_destroy(netdev, alink, opt->handle);
-               return 0;
-       case TC_RED_STATS:
-               return nfp_abm_red_stats(alink, opt);
-       case TC_RED_XSTATS:
-               return nfp_abm_red_xstats(alink, opt);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int
-nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt)
-{
-       struct nfp_alink_stats stats;
-       unsigned int i;
-       int err;
-
-       for (i = 0; i < alink->num_qdiscs; i++) {
-               if (alink->qdiscs[i].handle == TC_H_UNSPEC)
-                       continue;
-
-               err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
-               if (err)
-                       return err;
-
-               nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats,
-                                    &opt->stats);
-       }
-
-       return 0;
-}
-
-static int
-nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
-                   struct tc_mq_qopt_offload *opt)
-{
-       switch (opt->command) {
-       case TC_MQ_CREATE:
-               nfp_abm_reset_root(netdev, alink, opt->handle,
-                                  alink->total_queues);
-               return 0;
-       case TC_MQ_DESTROY:
-               if (opt->handle == alink->parent)
-                       nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
-               return 0;
-       case TC_MQ_STATS:
-               return nfp_abm_mq_stats(alink, opt);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int
 nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
                 enum tc_setup_type type, void *type_data)
 {
@@ -302,10 +38,16 @@ nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
                return -EOPNOTSUPP;
 
        switch (type) {
+       case TC_SETUP_ROOT_QDISC:
+               return nfp_abm_setup_root(netdev, repr->app_priv, type_data);
        case TC_SETUP_QDISC_MQ:
                return nfp_abm_setup_tc_mq(netdev, repr->app_priv, type_data);
        case TC_SETUP_QDISC_RED:
                return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data);
+       case TC_SETUP_QDISC_GRED:
+               return nfp_abm_setup_tc_gred(netdev, repr->app_priv, type_data);
+       case TC_SETUP_BLOCK:
+               return nfp_abm_setup_cls_block(netdev, repr, type_data);
        default:
                return -EOPNOTSUPP;
        }
@@ -384,7 +126,9 @@ nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink,
 
        reprs = nfp_reprs_get_locked(app, rtype);
        WARN(nfp_repr_get_locked(app, reprs, alink->id), "duplicate repr");
+       rtnl_lock();
        rcu_assign_pointer(reprs->reprs[alink->id], netdev);
+       rtnl_unlock();
 
        nfp_info(app->cpp, "%s Port %d Representor(%s) created\n",
                 ptype == NFP_PORT_PF_PORT ? "PCIe" : "Phys",
@@ -410,7 +154,9 @@ nfp_abm_kill_repr(struct nfp_app *app, struct nfp_abm_link *alink,
        netdev = nfp_repr_get_locked(app, reprs, alink->id);
        if (!netdev)
                return;
+       rtnl_lock();
        rcu_assign_pointer(reprs->reprs[alink->id], NULL);
+       rtnl_unlock();
        synchronize_rcu();
        /* Cast to make sure nfp_repr_clean_and_free() takes a nfp_repr */
        nfp_repr_clean_and_free((struct nfp_repr *)netdev_priv(netdev));
@@ -573,31 +319,34 @@ nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
        alink->abm = abm;
        alink->vnic = nn;
        alink->id = id;
-       alink->parent = TC_H_ROOT;
        alink->total_queues = alink->vnic->max_rx_rings;
-       alink->qdiscs = kvcalloc(alink->total_queues, sizeof(*alink->qdiscs),
-                                GFP_KERNEL);
-       if (!alink->qdiscs) {
-               err = -ENOMEM;
+
+       INIT_LIST_HEAD(&alink->dscp_map);
+
+       err = nfp_abm_ctrl_read_params(alink);
+       if (err)
+               goto err_free_alink;
+
+       alink->prio_map = kzalloc(abm->prio_map_len, GFP_KERNEL);
+       if (!alink->prio_map)
                goto err_free_alink;
-       }
 
        /* This is a multi-host app, make sure MAC/PHY is up, but don't
         * make the MAC/PHY state follow the state of any of the ports.
         */
        err = nfp_eth_set_configured(app->cpp, eth_port->index, true);
        if (err < 0)
-               goto err_free_qdiscs;
+               goto err_free_priomap;
 
        netif_keep_dst(nn->dp.netdev);
 
        nfp_abm_vnic_set_mac(app->pf, abm, nn, id);
-       nfp_abm_ctrl_read_params(alink);
+       INIT_RADIX_TREE(&alink->qdiscs, GFP_KERNEL);
 
        return 0;
 
-err_free_qdiscs:
-       kvfree(alink->qdiscs);
+err_free_priomap:
+       kfree(alink->prio_map);
 err_free_alink:
        kfree(alink);
        return err;
@@ -608,10 +357,20 @@ static void nfp_abm_vnic_free(struct nfp_app *app, struct nfp_net *nn)
        struct nfp_abm_link *alink = nn->app_priv;
 
        nfp_abm_kill_reprs(alink->abm, alink);
-       kvfree(alink->qdiscs);
+       WARN(!radix_tree_empty(&alink->qdiscs), "left over qdiscs\n");
+       kfree(alink->prio_map);
        kfree(alink);
 }
 
+static int nfp_abm_vnic_init(struct nfp_app *app, struct nfp_net *nn)
+{
+       struct nfp_abm_link *alink = nn->app_priv;
+
+       if (nfp_abm_has_prio(alink->abm))
+               return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
+       return 0;
+}
+
 static u64 *
 nfp_abm_port_get_stats(struct nfp_app *app, struct nfp_port *port, u64 *data)
 {
@@ -664,6 +423,7 @@ static int nfp_abm_init(struct nfp_app *app)
        struct nfp_pf *pf = app->pf;
        struct nfp_reprs *reprs;
        struct nfp_abm *abm;
+       unsigned int i;
        int err;
 
        if (!pf->eth_tbl) {
@@ -690,15 +450,35 @@ static int nfp_abm_init(struct nfp_app *app)
        if (err)
                goto err_free_abm;
 
+       err = -ENOMEM;
+       abm->num_thresholds = array_size(abm->num_bands, NFP_NET_MAX_RX_RINGS);
+       abm->threshold_undef = bitmap_zalloc(abm->num_thresholds, GFP_KERNEL);
+       if (!abm->threshold_undef)
+               goto err_free_abm;
+
+       abm->thresholds = kvcalloc(abm->num_thresholds,
+                                  sizeof(*abm->thresholds), GFP_KERNEL);
+       if (!abm->thresholds)
+               goto err_free_thresh_umap;
+       for (i = 0; i < abm->num_bands * NFP_NET_MAX_RX_RINGS; i++)
+               __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY);
+
+       abm->actions = kvcalloc(abm->num_thresholds, sizeof(*abm->actions),
+                               GFP_KERNEL);
+       if (!abm->actions)
+               goto err_free_thresh;
+       for (i = 0; i < abm->num_bands * NFP_NET_MAX_RX_RINGS; i++)
+               __nfp_abm_ctrl_set_q_act(abm, i, NFP_ABM_ACT_DROP);
+
        /* We start in legacy mode, make sure advanced queuing is disabled */
        err = nfp_abm_ctrl_qm_disable(abm);
        if (err)
-               goto err_free_abm;
+               goto err_free_act;
 
        err = -ENOMEM;
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
        if (!reprs)
-               goto err_free_abm;
+               goto err_free_act;
        RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs);
 
        reprs = nfp_reprs_alloc(pf->max_data_vnics);
@@ -710,6 +490,12 @@ static int nfp_abm_init(struct nfp_app *app)
 
 err_free_phys:
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+err_free_act:
+       kvfree(abm->actions);
+err_free_thresh:
+       kvfree(abm->thresholds);
+err_free_thresh_umap:
+       bitmap_free(abm->threshold_undef);
 err_free_abm:
        kfree(abm);
        app->priv = NULL;
@@ -723,6 +509,9 @@ static void nfp_abm_clean(struct nfp_app *app)
        nfp_abm_eswitch_clean_up(abm);
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF);
        nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+       bitmap_free(abm->threshold_undef);
+       kvfree(abm->actions);
+       kvfree(abm->thresholds);
        kfree(abm);
        app->priv = NULL;
 }
@@ -736,6 +525,7 @@ const struct nfp_app_type app_abm = {
 
        .vnic_alloc     = nfp_abm_vnic_alloc,
        .vnic_free      = nfp_abm_vnic_free,
+       .vnic_init      = nfp_abm_vnic_init,
 
        .port_get_stats         = nfp_abm_port_get_stats,
        .port_get_stats_count   = nfp_abm_port_get_stats_count,
index f907b7d..4dcf588 100644 (file)
@@ -4,7 +4,19 @@
 #ifndef __NFP_ABM_H__
 #define __NFP_ABM_H__ 1
 
+#include <linux/bits.h>
+#include <linux/list.h>
+#include <linux/radix-tree.h>
 #include <net/devlink.h>
+#include <net/pkt_cls.h>
+#include <net/pkt_sched.h>
+
+/* Dump of 64 PRIOs and 256 REDs seems to take 850us on Xeon v4 @ 2.20GHz;
+ * 2.5ms / 400Hz seems more than sufficient for stats resolution.
+ */
+#define NFP_ABM_STATS_REFRESH_IVAL     (2500 * 1000) /* ns */
+
+#define NFP_ABM_LVL_INFINITY           S32_MAX
 
 struct nfp_app;
 struct nfp_net;
@@ -12,21 +24,62 @@ struct nfp_net;
 #define NFP_ABM_PORTID_TYPE    GENMASK(23, 16)
 #define NFP_ABM_PORTID_ID      GENMASK(7, 0)
 
+/* The possible actions if thresholds are exceeded */
+enum nfp_abm_q_action {
+       /* mark if ECN capable, otherwise drop */
+       NFP_ABM_ACT_MARK_DROP           = 0,
+       /* mark if ECN capable, otherwise goto QM */
+       NFP_ABM_ACT_MARK_QUEUE          = 1,
+       NFP_ABM_ACT_DROP                = 2,
+       NFP_ABM_ACT_QUEUE               = 3,
+       NFP_ABM_ACT_NOQUEUE             = 4,
+};
+
 /**
  * struct nfp_abm - ABM NIC app structure
  * @app:       back pointer to nfp_app
  * @pf_id:     ID of our PF link
+ *
+ * @num_prios: number of supported DSCP priorities
+ * @num_bands: number of supported DSCP priority bands
+ * @action_mask:       bitmask of supported actions
+ *
+ * @thresholds:                current threshold configuration
+ * @threshold_undef:   bitmap of thresholds which have not been set
+ * @actions:           current FW action configuration
+ * @num_thresholds:    number of @thresholds and bits in @threshold_undef
+ *
+ * @prio_map_len:      computed length of FW priority map (in bytes)
+ * @dscp_mask:         mask FW will apply on DSCP field
+ *
  * @eswitch_mode:      devlink eswitch mode, advanced functions only visible
  *                     in switchdev mode
+ *
  * @q_lvls:    queue level control area
  * @qm_stats:  queue statistics symbol
+ * @q_stats:   basic queue statistics (only in per-band case)
  */
 struct nfp_abm {
        struct nfp_app *app;
        unsigned int pf_id;
+
+       unsigned int num_prios;
+       unsigned int num_bands;
+       unsigned int action_mask;
+
+       u32 *thresholds;
+       unsigned long *threshold_undef;
+       u8 *actions;
+       size_t num_thresholds;
+
+       unsigned int prio_map_len;
+       u8 dscp_mask;
+
        enum devlink_eswitch_mode eswitch_mode;
+
        const struct nfp_rtsym *q_lvls;
        const struct nfp_rtsym *qm_stats;
+       const struct nfp_rtsym *q_stats;
 };
 
 /**
@@ -57,16 +110,76 @@ struct nfp_alink_xstats {
        u64 pdrop;
 };
 
+enum nfp_qdisc_type {
+       NFP_QDISC_NONE = 0,
+       NFP_QDISC_MQ,
+       NFP_QDISC_RED,
+       NFP_QDISC_GRED,
+};
+
+#define NFP_QDISC_UNTRACKED    ((struct nfp_qdisc *)1UL)
+
 /**
- * struct nfp_red_qdisc - representation of single RED Qdisc
- * @handle:    handle of currently offloaded RED Qdisc
- * @stats:     statistics from last refresh
- * @xstats:    base of extended statistics
+ * struct nfp_qdisc - tracked TC Qdisc
+ * @netdev:            netdev on which Qdisc was created
+ * @type:              Qdisc type
+ * @handle:            handle of this Qdisc
+ * @parent_handle:     handle of the parent (unreliable if Qdisc was grafted)
+ * @use_cnt:           number of attachment points in the hierarchy
+ * @num_children:      current size of the @children array
+ * @children:          pointers to children
+ *
+ * @params_ok:         parameters of this Qdisc are OK for offload
+ * @offload_mark:      offload refresh state - selected for offload
+ * @offloaded:         Qdisc is currently offloaded to the HW
+ *
+ * @mq:                        MQ Qdisc specific parameters and state
+ * @mq.stats:          current stats of the MQ Qdisc
+ * @mq.prev_stats:     previously reported @mq.stats
+ *
+ * @red:               RED Qdisc specific parameters and state
+ * @red.num_bands:     Number of valid entries in the @red.band table
+ * @red.band:          Per-band array of RED instances
+ * @red.band.ecn:              ECN marking is enabled (rather than drop)
+ * @red.band.threshold:                ECN marking threshold
+ * @red.band.stats:            current stats of the RED Qdisc
+ * @red.band.prev_stats:       previously reported @red.stats
+ * @red.band.xstats:           extended stats for RED - current
+ * @red.band.prev_xstats:      extended stats for RED - previously reported
  */
-struct nfp_red_qdisc {
+struct nfp_qdisc {
+       struct net_device *netdev;
+       enum nfp_qdisc_type type;
        u32 handle;
-       struct nfp_alink_stats stats;
-       struct nfp_alink_xstats xstats;
+       u32 parent_handle;
+       unsigned int use_cnt;
+       unsigned int num_children;
+       struct nfp_qdisc **children;
+
+       bool params_ok;
+       bool offload_mark;
+       bool offloaded;
+
+       union {
+               /* NFP_QDISC_MQ */
+               struct {
+                       struct nfp_alink_stats stats;
+                       struct nfp_alink_stats prev_stats;
+               } mq;
+               /* TC_SETUP_QDISC_RED, TC_SETUP_QDISC_GRED */
+               struct {
+                       unsigned int num_bands;
+
+                       struct {
+                               bool ecn;
+                               u32 threshold;
+                               struct nfp_alink_stats stats;
+                               struct nfp_alink_stats prev_stats;
+                               struct nfp_alink_xstats xstats;
+                               struct nfp_alink_xstats prev_xstats;
+                       } band[MAX_DPs];
+               } red;
+       };
 };
 
 /**
@@ -76,9 +189,17 @@ struct nfp_red_qdisc {
  * @id:                id of the data vNIC
  * @queue_base:        id of base to host queue within PCIe (not QC idx)
  * @total_queues:      number of PF queues
- * @parent:    handle of expected parent, i.e. handle of MQ, or TC_H_ROOT
- * @num_qdiscs:        number of currently used qdiscs
- * @qdiscs:    array of qdiscs
+ *
+ * @last_stats_update: ktime of last stats update
+ *
+ * @prio_map:          current map of priorities
+ * @has_prio:          @prio_map is valid
+ *
+ * @def_band:          default band to use
+ * @dscp_map:          list of DSCP to band mappings
+ *
+ * @root_qdisc:        pointer to the current root of the Qdisc hierarchy
+ * @qdiscs:    all qdiscs recorded by major part of the handle
  */
 struct nfp_abm_link {
        struct nfp_abm *abm;
@@ -86,26 +207,65 @@ struct nfp_abm_link {
        unsigned int id;
        unsigned int queue_base;
        unsigned int total_queues;
-       u32 parent;
-       unsigned int num_qdiscs;
-       struct nfp_red_qdisc *qdiscs;
+
+       u64 last_stats_update;
+
+       u32 *prio_map;
+       bool has_prio;
+
+       u8 def_band;
+       struct list_head dscp_map;
+
+       struct nfp_qdisc *root_qdisc;
+       struct radix_tree_root qdiscs;
 };
 
-void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
+static inline bool nfp_abm_has_prio(struct nfp_abm *abm)
+{
+       return abm->num_bands > 1;
+}
+
+static inline bool nfp_abm_has_drop(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_DROP);
+}
+
+static inline bool nfp_abm_has_mark(struct nfp_abm *abm)
+{
+       return abm->action_mask & BIT(NFP_ABM_ACT_MARK_DROP);
+}
+
+void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink);
+int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct tc_root_qopt_offload *opt);
+int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
+                        struct tc_red_qopt_offload *opt);
+int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
+                       struct tc_mq_qopt_offload *opt);
+int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt);
+int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
+                           struct tc_block_offload *opt);
+
+int nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
 int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
-int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val);
-int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i,
-                          u32 val);
-int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
-                           struct nfp_alink_stats *stats);
-int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
+int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val);
+int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, u32 val);
+int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
+                            enum nfp_abm_q_action act);
+int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
+                          unsigned int queue, enum nfp_abm_q_action act);
+int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink,
+                             unsigned int band, unsigned int queue,
                              struct nfp_alink_stats *stats);
-int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
-                            struct nfp_alink_xstats *xstats);
-int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
+int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink,
+                              unsigned int band, unsigned int queue,
                               struct nfp_alink_xstats *xstats);
 u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i);
 u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i);
 int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm);
 int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm);
+void nfp_abm_prio_map_update(struct nfp_abm *abm);
+int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed);
 #endif
diff --git a/drivers/net/ethernet/netronome/nfp/abm/qdisc.c b/drivers/net/ethernet/netronome/nfp/abm/qdisc.c
new file mode 100644 (file)
index 0000000..2473fb5
--- /dev/null
@@ -0,0 +1,850 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2018 Netronome Systems, Inc. */
+
+#include <linux/rtnetlink.h>
+#include <net/pkt_cls.h>
+#include <net/pkt_sched.h>
+#include <net/red.h>
+
+#include "../nfpcore/nfp_cpp.h"
+#include "../nfp_app.h"
+#include "../nfp_main.h"
+#include "../nfp_net.h"
+#include "../nfp_port.h"
+#include "main.h"
+
+static bool nfp_abm_qdisc_is_red(struct nfp_qdisc *qdisc)
+{
+       return qdisc->type == NFP_QDISC_RED || qdisc->type == NFP_QDISC_GRED;
+}
+
+static bool nfp_abm_qdisc_child_valid(struct nfp_qdisc *qdisc, unsigned int id)
+{
+       return qdisc->children[id] &&
+              qdisc->children[id] != NFP_QDISC_UNTRACKED;
+}
+
+static void *nfp_abm_qdisc_tree_deref_slot(void __rcu **slot)
+{
+       return rtnl_dereference(*slot);
+}
+
+static void
+nfp_abm_stats_propagate(struct nfp_alink_stats *parent,
+                       struct nfp_alink_stats *child)
+{
+       parent->tx_pkts         += child->tx_pkts;
+       parent->tx_bytes        += child->tx_bytes;
+       parent->backlog_pkts    += child->backlog_pkts;
+       parent->backlog_bytes   += child->backlog_bytes;
+       parent->overlimits      += child->overlimits;
+       parent->drops           += child->drops;
+}
+
+static void
+nfp_abm_stats_update_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                        unsigned int queue)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       unsigned int i;
+       int err;
+
+       if (!qdisc->offloaded)
+               return;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               err = nfp_abm_ctrl_read_q_stats(alink, i, queue,
+                                               &qdisc->red.band[i].stats);
+               if (err)
+                       nfp_err(cpp, "RED stats (%d, %d) read failed with error %d\n",
+                               i, queue, err);
+
+               err = nfp_abm_ctrl_read_q_xstats(alink, i, queue,
+                                                &qdisc->red.band[i].xstats);
+               if (err)
+                       nfp_err(cpp, "RED xstats (%d, %d) read failed with error %d\n",
+                               i, queue, err);
+       }
+}
+
+static void
+nfp_abm_stats_update_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
+{
+       unsigned int i;
+
+       if (qdisc->type != NFP_QDISC_MQ)
+               return;
+
+       for (i = 0; i < alink->total_queues; i++)
+               if (nfp_abm_qdisc_child_valid(qdisc, i))
+                       nfp_abm_stats_update_red(alink, qdisc->children[i], i);
+}
+
+static void __nfp_abm_stats_update(struct nfp_abm_link *alink, u64 time_now)
+{
+       alink->last_stats_update = time_now;
+       if (alink->root_qdisc)
+               nfp_abm_stats_update_mq(alink, alink->root_qdisc);
+}
+
+static void nfp_abm_stats_update(struct nfp_abm_link *alink)
+{
+       u64 now;
+
+       /* Limit the frequency of updates - stats of non-leaf qdiscs are a sum
+        * of all their leafs, so we would read the same stat multiple times
+        * for every dump.
+        */
+       now = ktime_get();
+       if (now - alink->last_stats_update < NFP_ABM_STATS_REFRESH_IVAL)
+               return;
+
+       __nfp_abm_stats_update(alink, now);
+}
+
+static void
+nfp_abm_qdisc_unlink_children(struct nfp_qdisc *qdisc,
+                             unsigned int start, unsigned int end)
+{
+       unsigned int i;
+
+       for (i = start; i < end; i++)
+               if (nfp_abm_qdisc_child_valid(qdisc, i)) {
+                       qdisc->children[i]->use_cnt--;
+                       qdisc->children[i] = NULL;
+               }
+}
+
+static void
+nfp_abm_qdisc_offload_stop(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
+{
+       unsigned int i;
+
+       /* Don't complain when qdisc is getting unlinked */
+       if (qdisc->use_cnt)
+               nfp_warn(alink->abm->app->cpp, "Offload of '%08x' stopped\n",
+                        qdisc->handle);
+
+       if (!nfp_abm_qdisc_is_red(qdisc))
+               return;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               qdisc->red.band[i].stats.backlog_pkts = 0;
+               qdisc->red.band[i].stats.backlog_bytes = 0;
+       }
+}
+
+static int
+__nfp_abm_stats_init(struct nfp_abm_link *alink, unsigned int band,
+                    unsigned int queue, struct nfp_alink_stats *prev_stats,
+                    struct nfp_alink_xstats *prev_xstats)
+{
+       u64 backlog_pkts, backlog_bytes;
+       int err;
+
+       /* Don't touch the backlog, backlog can only be reset after it has
+        * been reported back to the tc qdisc stats.
+        */
+       backlog_pkts = prev_stats->backlog_pkts;
+       backlog_bytes = prev_stats->backlog_bytes;
+
+       err = nfp_abm_ctrl_read_q_stats(alink, band, queue, prev_stats);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "RED stats init (%d, %d) failed with error %d\n",
+                       band, queue, err);
+               return err;
+       }
+
+       err = nfp_abm_ctrl_read_q_xstats(alink, band, queue, prev_xstats);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "RED xstats init (%d, %d) failed with error %d\n",
+                       band, queue, err);
+               return err;
+       }
+
+       prev_stats->backlog_pkts = backlog_pkts;
+       prev_stats->backlog_bytes = backlog_bytes;
+       return 0;
+}
+
+static int
+nfp_abm_stats_init(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                  unsigned int queue)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               err = __nfp_abm_stats_init(alink, i, queue,
+                                          &qdisc->red.band[i].prev_stats,
+                                          &qdisc->red.band[i].prev_xstats);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void
+nfp_abm_offload_compile_red(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc,
+                           unsigned int queue)
+{
+       bool good_red, good_gred;
+       unsigned int i;
+
+       good_red = qdisc->type == NFP_QDISC_RED &&
+                  qdisc->params_ok &&
+                  qdisc->use_cnt == 1 &&
+                  !alink->has_prio &&
+                  !qdisc->children[0];
+       good_gred = qdisc->type == NFP_QDISC_GRED &&
+                   qdisc->params_ok &&
+                   qdisc->use_cnt == 1;
+       qdisc->offload_mark = good_red || good_gred;
+
+       /* If we are starting offload init prev_stats */
+       if (qdisc->offload_mark && !qdisc->offloaded)
+               if (nfp_abm_stats_init(alink, qdisc, queue))
+                       qdisc->offload_mark = false;
+
+       if (!qdisc->offload_mark)
+               return;
+
+       for (i = 0; i < alink->abm->num_bands; i++) {
+               enum nfp_abm_q_action act;
+
+               nfp_abm_ctrl_set_q_lvl(alink, i, queue,
+                                      qdisc->red.band[i].threshold);
+               act = qdisc->red.band[i].ecn ?
+                       NFP_ABM_ACT_MARK_DROP : NFP_ABM_ACT_DROP;
+               nfp_abm_ctrl_set_q_act(alink, i, queue, act);
+       }
+}
+
+static void
+nfp_abm_offload_compile_mq(struct nfp_abm_link *alink, struct nfp_qdisc *qdisc)
+{
+       unsigned int i;
+
+       qdisc->offload_mark = qdisc->type == NFP_QDISC_MQ;
+       if (!qdisc->offload_mark)
+               return;
+
+       for (i = 0; i < alink->total_queues; i++) {
+               struct nfp_qdisc *child = qdisc->children[i];
+
+               if (!nfp_abm_qdisc_child_valid(qdisc, i))
+                       continue;
+
+               nfp_abm_offload_compile_red(alink, child, i);
+       }
+}
+
+void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink)
+{
+       struct nfp_abm *abm = alink->abm;
+       struct radix_tree_iter iter;
+       struct nfp_qdisc *qdisc;
+       void __rcu **slot;
+       size_t i;
+
+       /* Mark all thresholds as unconfigured */
+       for (i = 0; i < abm->num_bands; i++)
+               __bitmap_set(abm->threshold_undef,
+                            i * NFP_NET_MAX_RX_RINGS + alink->queue_base,
+                            alink->total_queues);
+
+       /* Clear offload marks */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               qdisc = nfp_abm_qdisc_tree_deref_slot(slot);
+               qdisc->offload_mark = false;
+       }
+
+       if (alink->root_qdisc)
+               nfp_abm_offload_compile_mq(alink, alink->root_qdisc);
+
+       /* Refresh offload status */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               qdisc = nfp_abm_qdisc_tree_deref_slot(slot);
+               if (!qdisc->offload_mark && qdisc->offloaded)
+                       nfp_abm_qdisc_offload_stop(alink, qdisc);
+               qdisc->offloaded = qdisc->offload_mark;
+       }
+
+       /* Reset the unconfigured thresholds */
+       for (i = 0; i < abm->num_thresholds; i++)
+               if (test_bit(i, abm->threshold_undef))
+                       __nfp_abm_ctrl_set_q_lvl(abm, i, NFP_ABM_LVL_INFINITY);
+
+       __nfp_abm_stats_update(alink, ktime_get());
+}
+
+static void
+nfp_abm_qdisc_clear_mq(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct nfp_qdisc *qdisc)
+{
+       struct radix_tree_iter iter;
+       unsigned int mq_refs = 0;
+       void __rcu **slot;
+
+       if (!qdisc->use_cnt)
+               return;
+       /* MQ doesn't notify well on destruction, we need special handling of
+        * MQ's children.
+        */
+       if (qdisc->type == NFP_QDISC_MQ &&
+           qdisc == alink->root_qdisc &&
+           netdev->reg_state == NETREG_UNREGISTERING)
+               return;
+
+       /* Count refs held by MQ instances and clear pointers */
+       radix_tree_for_each_slot(slot, &alink->qdiscs, &iter, 0) {
+               struct nfp_qdisc *mq = nfp_abm_qdisc_tree_deref_slot(slot);
+               unsigned int i;
+
+               if (mq->type != NFP_QDISC_MQ || mq->netdev != netdev)
+                       continue;
+               for (i = 0; i < mq->num_children; i++)
+                       if (mq->children[i] == qdisc) {
+                               mq->children[i] = NULL;
+                               mq_refs++;
+                       }
+       }
+
+       WARN(qdisc->use_cnt != mq_refs, "non-zero qdisc use count: %d (- %d)\n",
+            qdisc->use_cnt, mq_refs);
+}
+
+static void
+nfp_abm_qdisc_free(struct net_device *netdev, struct nfp_abm_link *alink,
+                  struct nfp_qdisc *qdisc)
+{
+       struct nfp_port *port = nfp_port_from_netdev(netdev);
+
+       if (!qdisc)
+               return;
+       nfp_abm_qdisc_clear_mq(netdev, alink, qdisc);
+       WARN_ON(radix_tree_delete(&alink->qdiscs,
+                                 TC_H_MAJ(qdisc->handle)) != qdisc);
+
+       kfree(qdisc->children);
+       kfree(qdisc);
+
+       port->tc_offload_cnt--;
+}
+
+static struct nfp_qdisc *
+nfp_abm_qdisc_alloc(struct net_device *netdev, struct nfp_abm_link *alink,
+                   enum nfp_qdisc_type type, u32 parent_handle, u32 handle,
+                   unsigned int children)
+{
+       struct nfp_port *port = nfp_port_from_netdev(netdev);
+       struct nfp_qdisc *qdisc;
+       int err;
+
+       qdisc = kzalloc(sizeof(*qdisc), GFP_KERNEL);
+       if (!qdisc)
+               return NULL;
+
+       if (children) {
+               qdisc->children = kcalloc(children, sizeof(void *), GFP_KERNEL);
+               if (!qdisc->children)
+                       goto err_free_qdisc;
+       }
+
+       qdisc->netdev = netdev;
+       qdisc->type = type;
+       qdisc->parent_handle = parent_handle;
+       qdisc->handle = handle;
+       qdisc->num_children = children;
+
+       err = radix_tree_insert(&alink->qdiscs, TC_H_MAJ(qdisc->handle), qdisc);
+       if (err) {
+               nfp_err(alink->abm->app->cpp,
+                       "Qdisc insertion into radix tree failed: %d\n", err);
+               goto err_free_child_tbl;
+       }
+
+       port->tc_offload_cnt++;
+       return qdisc;
+
+err_free_child_tbl:
+       kfree(qdisc->children);
+err_free_qdisc:
+       kfree(qdisc);
+       return NULL;
+}
+
+static struct nfp_qdisc *
+nfp_abm_qdisc_find(struct nfp_abm_link *alink, u32 handle)
+{
+       return radix_tree_lookup(&alink->qdiscs, TC_H_MAJ(handle));
+}
+
+static int
+nfp_abm_qdisc_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                     enum nfp_qdisc_type type, u32 parent_handle, u32 handle,
+                     unsigned int children, struct nfp_qdisc **qdisc)
+{
+       *qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (*qdisc) {
+               if (WARN_ON((*qdisc)->type != type))
+                       return -EINVAL;
+               return 1;
+       }
+
+       *qdisc = nfp_abm_qdisc_alloc(netdev, alink, type, parent_handle, handle,
+                                    children);
+       return *qdisc ? 0 : -ENOMEM;
+}
+
+static void
+nfp_abm_qdisc_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
+                     u32 handle)
+{
+       struct nfp_qdisc *qdisc;
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return;
+
+       /* We don't get TC_SETUP_ROOT_QDISC w/ MQ when netdev is unregistered */
+       if (alink->root_qdisc == qdisc)
+               qdisc->use_cnt--;
+
+       nfp_abm_qdisc_unlink_children(qdisc, 0, qdisc->num_children);
+       nfp_abm_qdisc_free(netdev, alink, qdisc);
+
+       if (alink->root_qdisc == qdisc) {
+               alink->root_qdisc = NULL;
+               /* Only root change matters, other changes are acted upon on
+                * the graft notification.
+                */
+               nfp_abm_qdisc_offload_update(alink);
+       }
+}
+
+static int
+nfp_abm_qdisc_graft(struct nfp_abm_link *alink, u32 handle, u32 child_handle,
+                   unsigned int id)
+{
+       struct nfp_qdisc *parent, *child;
+
+       parent = nfp_abm_qdisc_find(alink, handle);
+       if (!parent)
+               return 0;
+
+       if (WARN(id >= parent->num_children,
+                "graft child out of bound %d >= %d\n",
+                id, parent->num_children))
+               return -EINVAL;
+
+       nfp_abm_qdisc_unlink_children(parent, id, id + 1);
+
+       child = nfp_abm_qdisc_find(alink, child_handle);
+       if (child)
+               child->use_cnt++;
+       else
+               child = NFP_QDISC_UNTRACKED;
+       parent->children[id] = child;
+
+       nfp_abm_qdisc_offload_update(alink);
+
+       return 0;
+}
+
+static void
+nfp_abm_stats_calculate(struct nfp_alink_stats *new,
+                       struct nfp_alink_stats *old,
+                       struct gnet_stats_basic_packed *bstats,
+                       struct gnet_stats_queue *qstats)
+{
+       _bstats_update(bstats, new->tx_bytes - old->tx_bytes,
+                      new->tx_pkts - old->tx_pkts);
+       qstats->qlen += new->backlog_pkts - old->backlog_pkts;
+       qstats->backlog += new->backlog_bytes - old->backlog_bytes;
+       qstats->overlimits += new->overlimits - old->overlimits;
+       qstats->drops += new->drops - old->drops;
+}
+
+static void
+nfp_abm_stats_red_calculate(struct nfp_alink_xstats *new,
+                           struct nfp_alink_xstats *old,
+                           struct red_stats *stats)
+{
+       stats->forced_mark += new->ecn_marked - old->ecn_marked;
+       stats->pdrop += new->pdrop - old->pdrop;
+}
+
+static int
+nfp_abm_gred_stats(struct nfp_abm_link *alink, u32 handle,
+                  struct tc_gred_qopt_offload_stats *stats)
+{
+       struct nfp_qdisc *qdisc;
+       unsigned int i;
+
+       nfp_abm_stats_update(alink);
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+       /* If the qdisc offload has stopped we may need to adjust the backlog
+        * counters back so carry on even if qdisc is not currently offloaded.
+        */
+
+       for (i = 0; i < qdisc->red.num_bands; i++) {
+               if (!stats->xstats[i])
+                       continue;
+
+               nfp_abm_stats_calculate(&qdisc->red.band[i].stats,
+                                       &qdisc->red.band[i].prev_stats,
+                                       &stats->bstats[i], &stats->qstats[i]);
+               qdisc->red.band[i].prev_stats = qdisc->red.band[i].stats;
+
+               nfp_abm_stats_red_calculate(&qdisc->red.band[i].xstats,
+                                           &qdisc->red.band[i].prev_xstats,
+                                           stats->xstats[i]);
+               qdisc->red.band[i].prev_xstats = qdisc->red.band[i].xstats;
+       }
+
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
+}
+
+static bool
+nfp_abm_gred_check_params(struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       struct nfp_abm *abm = alink->abm;
+       unsigned int i;
+
+       if (opt->set.grio_on || opt->set.wred_on) {
+               nfp_warn(cpp, "GRED offload failed - GRIO and WRED not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.dp_def != alink->def_band) {
+               nfp_warn(cpp, "GRED offload failed - default band must be %d (p:%08x h:%08x)\n",
+                        alink->def_band, opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.dp_cnt != abm->num_bands) {
+               nfp_warn(cpp, "GRED offload failed - band count must be %d (p:%08x h:%08x)\n",
+                        abm->num_bands, opt->parent, opt->handle);
+               return false;
+       }
+
+       for (i = 0; i < abm->num_bands; i++) {
+               struct tc_gred_vq_qopt_offload_params *band = &opt->set.tab[i];
+
+               if (!band->present)
+                       return false;
+               if (!band->is_ecn && !nfp_abm_has_drop(abm)) {
+                       nfp_warn(cpp, "GRED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->is_ecn && !nfp_abm_has_mark(abm)) {
+                       nfp_warn(cpp, "GRED offload failed - ECN marking not supported (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->is_harddrop) {
+                       nfp_warn(cpp, "GRED offload failed - harddrop is not supported (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->min != band->max) {
+                       nfp_warn(cpp, "GRED offload failed - threshold mismatch (p:%08x h:%08x vq:%d)\n",
+                                opt->parent, opt->handle, i);
+                       return false;
+               }
+               if (band->min > S32_MAX) {
+                       nfp_warn(cpp, "GRED offload failed - threshold too large %d > %d (p:%08x h:%08x vq:%d)\n",
+                                band->min, S32_MAX, opt->parent, opt->handle,
+                                i);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static int
+nfp_abm_gred_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                    struct tc_gred_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+       unsigned int i;
+       int ret;
+
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_GRED, opt->parent,
+                                   opt->handle, 0, &qdisc);
+       if (ret < 0)
+               return ret;
+
+       qdisc->params_ok = nfp_abm_gred_check_params(alink, opt);
+       if (qdisc->params_ok) {
+               qdisc->red.num_bands = opt->set.dp_cnt;
+               for (i = 0; i < qdisc->red.num_bands; i++) {
+                       qdisc->red.band[i].ecn = opt->set.tab[i].is_ecn;
+                       qdisc->red.band[i].threshold = opt->set.tab[i].min;
+               }
+       }
+
+       if (qdisc->use_cnt)
+               nfp_abm_qdisc_offload_update(alink);
+
+       return 0;
+}
+
+int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink,
+                         struct tc_gred_qopt_offload *opt)
+{
+       switch (opt->command) {
+       case TC_GRED_REPLACE:
+               return nfp_abm_gred_replace(netdev, alink, opt);
+       case TC_GRED_DESTROY:
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
+               return 0;
+       case TC_GRED_STATS:
+               return nfp_abm_gred_stats(alink, opt->handle, &opt->stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+
+       nfp_abm_stats_update(alink);
+
+       qdisc = nfp_abm_qdisc_find(alink, opt->handle);
+       if (!qdisc || !qdisc->offloaded)
+               return -EOPNOTSUPP;
+
+       nfp_abm_stats_red_calculate(&qdisc->red.band[0].xstats,
+                                   &qdisc->red.band[0].prev_xstats,
+                                   opt->xstats);
+       qdisc->red.band[0].prev_xstats = qdisc->red.band[0].xstats;
+       return 0;
+}
+
+static int
+nfp_abm_red_stats(struct nfp_abm_link *alink, u32 handle,
+                 struct tc_qopt_offload_stats *stats)
+{
+       struct nfp_qdisc *qdisc;
+
+       nfp_abm_stats_update(alink);
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+       /* If the qdisc offload has stopped we may need to adjust the backlog
+        * counters back so carry on even if qdisc is not currently offloaded.
+        */
+
+       nfp_abm_stats_calculate(&qdisc->red.band[0].stats,
+                               &qdisc->red.band[0].prev_stats,
+                               stats->bstats, stats->qstats);
+       qdisc->red.band[0].prev_stats = qdisc->red.band[0].stats;
+
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
+}
+
+static bool
+nfp_abm_red_check_params(struct nfp_abm_link *alink,
+                        struct tc_red_qopt_offload *opt)
+{
+       struct nfp_cpp *cpp = alink->abm->app->cpp;
+       struct nfp_abm *abm = alink->abm;
+
+       if (!opt->set.is_ecn && !nfp_abm_has_drop(abm)) {
+               nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.is_ecn && !nfp_abm_has_mark(abm)) {
+               nfp_warn(cpp, "RED offload failed - ECN marking not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.is_harddrop) {
+               nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.min != opt->set.max) {
+               nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n",
+                        opt->parent, opt->handle);
+               return false;
+       }
+       if (opt->set.min > NFP_ABM_LVL_INFINITY) {
+               nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n",
+                        opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent,
+                        opt->handle);
+               return false;
+       }
+
+       return true;
+}
+
+static int
+nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
+                   struct tc_red_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+       int ret;
+
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_RED, opt->parent,
+                                   opt->handle, 1, &qdisc);
+       if (ret < 0)
+               return ret;
+
+       /* If limit != 0 child gets reset */
+       if (opt->set.limit) {
+               if (nfp_abm_qdisc_child_valid(qdisc, 0))
+                       qdisc->children[0]->use_cnt--;
+               qdisc->children[0] = NULL;
+       } else {
+               /* Qdisc was just allocated without a limit will use noop_qdisc,
+                * i.e. a block hole.
+                */
+               if (!ret)
+                       qdisc->children[0] = NFP_QDISC_UNTRACKED;
+       }
+
+       qdisc->params_ok = nfp_abm_red_check_params(alink, opt);
+       if (qdisc->params_ok) {
+               qdisc->red.num_bands = 1;
+               qdisc->red.band[0].ecn = opt->set.is_ecn;
+               qdisc->red.band[0].threshold = opt->set.min;
+       }
+
+       if (qdisc->use_cnt == 1)
+               nfp_abm_qdisc_offload_update(alink);
+
+       return 0;
+}
+
+int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
+                        struct tc_red_qopt_offload *opt)
+{
+       switch (opt->command) {
+       case TC_RED_REPLACE:
+               return nfp_abm_red_replace(netdev, alink, opt);
+       case TC_RED_DESTROY:
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
+               return 0;
+       case TC_RED_STATS:
+               return nfp_abm_red_stats(alink, opt->handle, &opt->stats);
+       case TC_RED_XSTATS:
+               return nfp_abm_red_xstats(alink, opt);
+       case TC_RED_GRAFT:
+               return nfp_abm_qdisc_graft(alink, opt->handle,
+                                          opt->child_handle, 0);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+nfp_abm_mq_create(struct net_device *netdev, struct nfp_abm_link *alink,
+                 struct tc_mq_qopt_offload *opt)
+{
+       struct nfp_qdisc *qdisc;
+       int ret;
+
+       ret = nfp_abm_qdisc_replace(netdev, alink, NFP_QDISC_MQ,
+                                   TC_H_ROOT, opt->handle, alink->total_queues,
+                                   &qdisc);
+       if (ret < 0)
+               return ret;
+
+       qdisc->params_ok = true;
+       qdisc->offloaded = true;
+       nfp_abm_qdisc_offload_update(alink);
+       return 0;
+}
+
+static int
+nfp_abm_mq_stats(struct nfp_abm_link *alink, u32 handle,
+                struct tc_qopt_offload_stats *stats)
+{
+       struct nfp_qdisc *qdisc, *red;
+       unsigned int i, j;
+
+       qdisc = nfp_abm_qdisc_find(alink, handle);
+       if (!qdisc)
+               return -EOPNOTSUPP;
+
+       nfp_abm_stats_update(alink);
+
+       /* MQ stats are summed over the children in the core, so we need
+        * to add up the unreported child values.
+        */
+       memset(&qdisc->mq.stats, 0, sizeof(qdisc->mq.stats));
+       memset(&qdisc->mq.prev_stats, 0, sizeof(qdisc->mq.prev_stats));
+
+       for (i = 0; i < qdisc->num_children; i++) {
+               if (!nfp_abm_qdisc_child_valid(qdisc, i))
+                       continue;
+
+               if (!nfp_abm_qdisc_is_red(qdisc->children[i]))
+                       continue;
+               red = qdisc->children[i];
+
+               for (j = 0; j < red->red.num_bands; j++) {
+                       nfp_abm_stats_propagate(&qdisc->mq.stats,
+                                               &red->red.band[j].stats);
+                       nfp_abm_stats_propagate(&qdisc->mq.prev_stats,
+                                               &red->red.band[j].prev_stats);
+               }
+       }
+
+       nfp_abm_stats_calculate(&qdisc->mq.stats, &qdisc->mq.prev_stats,
+                               stats->bstats, stats->qstats);
+
+       return qdisc->offloaded ? 0 : -EOPNOTSUPP;
+}
+
+int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
+                       struct tc_mq_qopt_offload *opt)
+{
+       switch (opt->command) {
+       case TC_MQ_CREATE:
+               return nfp_abm_mq_create(netdev, alink, opt);
+       case TC_MQ_DESTROY:
+               nfp_abm_qdisc_destroy(netdev, alink, opt->handle);
+               return 0;
+       case TC_MQ_STATS:
+               return nfp_abm_mq_stats(alink, opt->handle, &opt->stats);
+       case TC_MQ_GRAFT:
+               return nfp_abm_qdisc_graft(alink, opt->handle,
+                                          opt->graft_params.child_handle,
+                                          opt->graft_params.queue);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
+                      struct tc_root_qopt_offload *opt)
+{
+       if (opt->ingress)
+               return -EOPNOTSUPP;
+       if (alink->root_qdisc)
+               alink->root_qdisc->use_cnt--;
+       alink->root_qdisc = nfp_abm_qdisc_find(alink, opt->handle);
+       if (alink->root_qdisc)
+               alink->root_qdisc->use_cnt++;
+
+       nfp_abm_qdisc_offload_update(alink);
+
+       return 0;
+}
index 6243af0..dccae03 100644 (file)
@@ -465,7 +465,7 @@ static int nfp_bpf_init(struct nfp_app *app)
                app->ctrl_mtu = nfp_bpf_ctrl_cmsg_mtu(bpf);
        }
 
-       bpf->bpf_dev = bpf_offload_dev_create();
+       bpf->bpf_dev = bpf_offload_dev_create(&nfp_bpf_dev_ops);
        err = PTR_ERR_OR_ZERO(bpf->bpf_dev);
        if (err)
                goto err_free_neutral_maps;
index 7f591d7..9412779 100644 (file)
@@ -509,7 +509,11 @@ void nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog, unsigned int cnt);
 int nfp_bpf_jit(struct nfp_prog *prog);
 bool nfp_bpf_supported_opcode(u8 code);
 
-extern const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops;
+int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
+                   int prev_insn_idx);
+int nfp_bpf_finalize(struct bpf_verifier_env *env);
+
+extern const struct bpf_prog_offload_ops nfp_bpf_dev_ops;
 
 struct netdev_bpf;
 struct nfp_app;
index ba8ceed..f028385 100644 (file)
@@ -33,9 +33,6 @@ nfp_map_ptr_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog,
        struct nfp_bpf_neutral_map *record;
        int err;
 
-       /* Map record paths are entered via ndo, update side is protected. */
-       ASSERT_RTNL();
-
        /* Reuse path - other offloaded program is already tracking this map. */
        record = rhashtable_lookup_fast(&bpf->maps_neutral, &map->id,
                                        nfp_bpf_maps_neutral_params);
@@ -84,8 +81,6 @@ nfp_map_ptrs_forget(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog)
        bool freed = false;
        int i;
 
-       ASSERT_RTNL();
-
        for (i = 0; i < nfp_prog->map_records_cnt; i++) {
                if (--nfp_prog->map_records[i]->count) {
                        nfp_prog->map_records[i] = NULL;
@@ -187,11 +182,10 @@ static void nfp_prog_free(struct nfp_prog *nfp_prog)
        kfree(nfp_prog);
 }
 
-static int
-nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
-                     struct netdev_bpf *bpf)
+static int nfp_bpf_verifier_prep(struct bpf_prog *prog)
 {
-       struct bpf_prog *prog = bpf->verifier.prog;
+       struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev);
+       struct nfp_app *app = nn->app;
        struct nfp_prog *nfp_prog;
        int ret;
 
@@ -209,7 +203,6 @@ nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
                goto err_free;
 
        nfp_prog->verifier_meta = nfp_prog_first_meta(nfp_prog);
-       bpf->verifier.ops = &nfp_bpf_analyzer_ops;
 
        return 0;
 
@@ -219,8 +212,9 @@ err_free:
        return ret;
 }
 
-static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)
+static int nfp_bpf_translate(struct bpf_prog *prog)
 {
+       struct nfp_net *nn = netdev_priv(prog->aux->offload->netdev);
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
        unsigned int max_instr;
        int err;
@@ -242,15 +236,13 @@ static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)
        return nfp_map_ptrs_record(nfp_prog->bpf, nfp_prog, prog);
 }
 
-static int nfp_bpf_destroy(struct nfp_net *nn, struct bpf_prog *prog)
+static void nfp_bpf_destroy(struct bpf_prog *prog)
 {
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
 
        kvfree(nfp_prog->prog);
        nfp_map_ptrs_forget(nfp_prog->bpf, nfp_prog);
        nfp_prog_free(nfp_prog);
-
-       return 0;
 }
 
 /* Atomic engine requires values to be in big endian, we need to byte swap
@@ -422,12 +414,6 @@ nfp_bpf_map_free(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
 int nfp_ndo_bpf(struct nfp_app *app, struct nfp_net *nn, struct netdev_bpf *bpf)
 {
        switch (bpf->command) {
-       case BPF_OFFLOAD_VERIFIER_PREP:
-               return nfp_bpf_verifier_prep(app, nn, bpf);
-       case BPF_OFFLOAD_TRANSLATE:
-               return nfp_bpf_translate(nn, bpf->offload.prog);
-       case BPF_OFFLOAD_DESTROY:
-               return nfp_bpf_destroy(nn, bpf->offload.prog);
        case BPF_OFFLOAD_MAP_ALLOC:
                return nfp_bpf_map_alloc(app->priv, bpf->offmap);
        case BPF_OFFLOAD_MAP_FREE:
@@ -489,14 +475,15 @@ nfp_net_bpf_load(struct nfp_net *nn, struct bpf_prog *prog,
                 struct netlink_ext_ack *extack)
 {
        struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
-       unsigned int max_mtu, max_stack, max_prog_len;
+       unsigned int fw_mtu, pkt_off, max_stack, max_prog_len;
        dma_addr_t dma_addr;
        void *img;
        int err;
 
-       max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;
-       if (max_mtu < nn->dp.netdev->mtu) {
-               NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with MTU larger than HW packet split boundary");
+       fw_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32;
+       pkt_off = min(prog->aux->max_pkt_offset, nn->dp.netdev->mtu);
+       if (fw_mtu < pkt_off) {
+               NL_SET_ERR_MSG_MOD(extack, "BPF offload not supported with potential packet access beyond HW packet split boundary");
                return -EOPNOTSUPP;
        }
 
@@ -600,3 +587,11 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,
 
        return 0;
 }
+
+const struct bpf_prog_offload_ops nfp_bpf_dev_ops = {
+       .insn_hook      = nfp_verify_insn,
+       .finalize       = nfp_bpf_finalize,
+       .prepare        = nfp_bpf_verifier_prep,
+       .translate      = nfp_bpf_translate,
+       .destroy        = nfp_bpf_destroy,
+};
index 99f977b..337bb86 100644 (file)
@@ -623,8 +623,8 @@ nfp_bpf_check_alu(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
        return 0;
 }
 
-static int
-nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
+int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
+                   int prev_insn_idx)
 {
        struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv;
        struct nfp_insn_meta *meta = nfp_prog->verifier_meta;
@@ -745,7 +745,7 @@ continue_subprog:
        goto continue_subprog;
 }
 
-static int nfp_bpf_finalize(struct bpf_verifier_env *env)
+int nfp_bpf_finalize(struct bpf_verifier_env *env)
 {
        struct bpf_subprog_info *info;
        struct nfp_prog *nfp_prog;
@@ -788,8 +788,3 @@ static int nfp_bpf_finalize(struct bpf_verifier_env *env)
 
        return 0;
 }
-
-const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = {
-       .insn_hook      = nfp_verify_insn,
-       .finalize       = nfp_bpf_finalize,
-};
index 244dc26..8d54b36 100644 (file)
@@ -2,7 +2,6 @@
 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
 
 #include <linux/bitfield.h>
-#include <net/geneve.h>
 #include <net/pkt_cls.h>
 #include <net/switchdev.h>
 #include <net/tc_act/tc_csum.h>
@@ -91,21 +90,6 @@ nfp_fl_pre_lag(struct nfp_app *app, const struct tc_action *action,
        return act_size;
 }
 
-static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev,
-                                        enum nfp_flower_tun_type tun_type)
-{
-       if (!out_dev->rtnl_link_ops)
-               return false;
-
-       if (!strcmp(out_dev->rtnl_link_ops->kind, "vxlan"))
-               return tun_type == NFP_FL_TUNNEL_VXLAN;
-
-       if (!strcmp(out_dev->rtnl_link_ops->kind, "geneve"))
-               return tun_type == NFP_FL_TUNNEL_GENEVE;
-
-       return false;
-}
-
 static int
 nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
              const struct tc_action *action, struct nfp_fl_payload *nfp_flow,
@@ -151,11 +135,12 @@ nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
                /* Set action output parameters. */
                output->flags = cpu_to_be16(tmp_flags);
 
-               /* Only offload if egress ports are on the same device as the
-                * ingress port.
-                */
-               if (!switchdev_port_same_parent_id(in_dev, out_dev))
-                       return -EOPNOTSUPP;
+               if (nfp_netdev_is_nfp_repr(in_dev)) {
+                       /* Confirm ingress and egress are on same device. */
+                       if (!switchdev_port_same_parent_id(in_dev, out_dev))
+                               return -EOPNOTSUPP;
+               }
+
                if (!nfp_netdev_is_nfp_repr(out_dev))
                        return -EOPNOTSUPP;
 
@@ -384,10 +369,21 @@ nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
        return 0;
 }
 
+struct ipv4_ttl_word {
+       __u8    ttl;
+       __u8    protocol;
+       __sum16 check;
+};
+
 static int
 nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
-              struct nfp_fl_set_ip4_addrs *set_ip_addr)
+              struct nfp_fl_set_ip4_addrs *set_ip_addr,
+              struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos)
 {
+       struct ipv4_ttl_word *ttl_word_mask;
+       struct ipv4_ttl_word *ttl_word;
+       struct iphdr *tos_word_mask;
+       struct iphdr *tos_word;
        __be32 exact, mask;
 
        /* We are expecting tcf_pedit to return a big endian value */
@@ -402,20 +398,53 @@ nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
                set_ip_addr->ipv4_dst_mask |= mask;
                set_ip_addr->ipv4_dst &= ~mask;
                set_ip_addr->ipv4_dst |= exact & mask;
+               set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
+               set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
+                                          NFP_FL_LW_SIZ;
                break;
        case offsetof(struct iphdr, saddr):
                set_ip_addr->ipv4_src_mask |= mask;
                set_ip_addr->ipv4_src &= ~mask;
                set_ip_addr->ipv4_src |= exact & mask;
+               set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
+               set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
+                                          NFP_FL_LW_SIZ;
+               break;
+       case offsetof(struct iphdr, ttl):
+               ttl_word_mask = (struct ipv4_ttl_word *)&mask;
+               ttl_word = (struct ipv4_ttl_word *)&exact;
+
+               if (ttl_word_mask->protocol || ttl_word_mask->check)
+                       return -EOPNOTSUPP;
+
+               set_ip_ttl_tos->ipv4_ttl_mask |= ttl_word_mask->ttl;
+               set_ip_ttl_tos->ipv4_ttl &= ~ttl_word_mask->ttl;
+               set_ip_ttl_tos->ipv4_ttl |= ttl_word->ttl & ttl_word_mask->ttl;
+               set_ip_ttl_tos->head.jump_id =
+                       NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
+               set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
+                                             NFP_FL_LW_SIZ;
+               break;
+       case round_down(offsetof(struct iphdr, tos), 4):
+               tos_word_mask = (struct iphdr *)&mask;
+               tos_word = (struct iphdr *)&exact;
+
+               if (tos_word_mask->version || tos_word_mask->ihl ||
+                   tos_word_mask->tot_len)
+                       return -EOPNOTSUPP;
+
+               set_ip_ttl_tos->ipv4_tos_mask |= tos_word_mask->tos;
+               set_ip_ttl_tos->ipv4_tos &= ~tos_word_mask->tos;
+               set_ip_ttl_tos->ipv4_tos |= tos_word->tos & tos_word_mask->tos;
+               set_ip_ttl_tos->head.jump_id =
+                       NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
+               set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
+                                             NFP_FL_LW_SIZ;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       set_ip_addr->reserved = cpu_to_be16(0);
-       set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
-       set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> NFP_FL_LW_SIZ;
-
        return 0;
 }
 
@@ -432,12 +461,57 @@ nfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask,
        ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
 }
 
+struct ipv6_hop_limit_word {
+       __be16 payload_len;
+       u8 nexthdr;
+       u8 hop_limit;
+};
+
+static int
+nfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask,
+                                   struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl)
+{
+       struct ipv6_hop_limit_word *fl_hl_mask;
+       struct ipv6_hop_limit_word *fl_hl;
+
+       switch (off) {
+       case offsetof(struct ipv6hdr, payload_len):
+               fl_hl_mask = (struct ipv6_hop_limit_word *)&mask;
+               fl_hl = (struct ipv6_hop_limit_word *)&exact;
+
+               if (fl_hl_mask->nexthdr || fl_hl_mask->payload_len)
+                       return -EOPNOTSUPP;
+
+               ip_hl_fl->ipv6_hop_limit_mask |= fl_hl_mask->hop_limit;
+               ip_hl_fl->ipv6_hop_limit &= ~fl_hl_mask->hop_limit;
+               ip_hl_fl->ipv6_hop_limit |= fl_hl->hop_limit &
+                                           fl_hl_mask->hop_limit;
+               break;
+       case round_down(offsetof(struct ipv6hdr, flow_lbl), 4):
+               if (mask & ~IPV6_FLOW_LABEL_MASK ||
+                   exact & ~IPV6_FLOW_LABEL_MASK)
+                       return -EOPNOTSUPP;
+
+               ip_hl_fl->ipv6_label_mask |= mask;
+               ip_hl_fl->ipv6_label &= ~mask;
+               ip_hl_fl->ipv6_label |= exact & mask;
+               break;
+       }
+
+       ip_hl_fl->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL;
+       ip_hl_fl->head.len_lw = sizeof(*ip_hl_fl) >> NFP_FL_LW_SIZ;
+
+       return 0;
+}
+
 static int
 nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
               struct nfp_fl_set_ipv6_addr *ip_dst,
-              struct nfp_fl_set_ipv6_addr *ip_src)
+              struct nfp_fl_set_ipv6_addr *ip_src,
+              struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl)
 {
        __be32 exact, mask;
+       int err = 0;
        u8 word;
 
        /* We are expecting tcf_pedit to return a big endian value */
@@ -448,7 +522,8 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
                return -EOPNOTSUPP;
 
        if (off < offsetof(struct ipv6hdr, saddr)) {
-               return -EOPNOTSUPP;
+               err = nfp_fl_set_ip6_hop_limit_flow_label(off, exact, mask,
+                                                         ip_hl_fl);
        } else if (off < offsetof(struct ipv6hdr, daddr)) {
                word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact);
                nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word,
@@ -462,7 +537,7 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
                return -EOPNOTSUPP;
        }
 
-       return 0;
+       return err;
 }
 
 static int
@@ -513,6 +588,8 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
             char *nfp_action, int *a_len, u32 *csum_updated)
 {
        struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
+       struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl;
+       struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos;
        struct nfp_fl_set_ip4_addrs set_ip_addr;
        struct nfp_fl_set_tport set_tport;
        struct nfp_fl_set_eth set_eth;
@@ -522,6 +599,8 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
        u32 offset, cmd;
        u8 ip_proto = 0;
 
+       memset(&set_ip6_tc_hl_fl, 0, sizeof(set_ip6_tc_hl_fl));
+       memset(&set_ip_ttl_tos, 0, sizeof(set_ip_ttl_tos));
        memset(&set_ip6_dst, 0, sizeof(set_ip6_dst));
        memset(&set_ip6_src, 0, sizeof(set_ip6_src));
        memset(&set_ip_addr, 0, sizeof(set_ip_addr));
@@ -542,11 +621,12 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                        err = nfp_fl_set_eth(action, idx, offset, &set_eth);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
-                       err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr);
+                       err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr,
+                                            &set_ip_ttl_tos);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
                        err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
-                                            &set_ip6_src);
+                                            &set_ip6_src, &set_ip6_tc_hl_fl);
                        break;
                case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
                        err = nfp_fl_set_tport(action, idx, offset, &set_tport,
@@ -577,6 +657,16 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                memcpy(nfp_action, &set_eth, act_size);
                *a_len += act_size;
        }
+       if (set_ip_ttl_tos.head.len_lw) {
+               nfp_action += act_size;
+               act_size = sizeof(set_ip_ttl_tos);
+               memcpy(nfp_action, &set_ip_ttl_tos, act_size);
+               *a_len += act_size;
+
+               /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
+               *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
+                               nfp_fl_csum_l4_to_flag(ip_proto);
+       }
        if (set_ip_addr.head.len_lw) {
                nfp_action += act_size;
                act_size = sizeof(set_ip_addr);
@@ -587,6 +677,15 @@ nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow,
                *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
                                nfp_fl_csum_l4_to_flag(ip_proto);
        }
+       if (set_ip6_tc_hl_fl.head.len_lw) {
+               nfp_action += act_size;
+               act_size = sizeof(set_ip6_tc_hl_fl);
+               memcpy(nfp_action, &set_ip6_tc_hl_fl, act_size);
+               *a_len += act_size;
+
+               /* Hardware will automatically fix TCP/UDP checksum. */
+               *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
+       }
        if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) {
                /* TC compiles set src and dst IPv6 address as a single action,
                 * the hardware requires this to be 2 separate actions.
@@ -728,9 +827,8 @@ nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a,
                *a_len += sizeof(struct nfp_fl_push_vlan);
        } else if (is_tcf_tunnel_set(a)) {
                struct ip_tunnel_info *ip_tun = tcf_tunnel_info(a);
-               struct nfp_repr *repr = netdev_priv(netdev);
 
-               *tun_type = nfp_fl_get_tun_from_act_l4_port(repr->app, a);
+               *tun_type = nfp_fl_get_tun_from_act_l4_port(app, a);
                if (*tun_type == NFP_FL_TUNNEL_NONE)
                        return -EOPNOTSUPP;
 
index 29d673a..15f41cf 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/skbuff.h>
 #include <linux/types.h>
 #include <net/geneve.h>
+#include <net/vxlan.h>
 
 #include "../nfp_app.h"
 #include "../nfpcore/nfp_cpp.h"
 #define NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL   6
 #define NFP_FL_ACTION_OPCODE_SET_ETHERNET      7
 #define NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS    9
+#define NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS  10
 #define NFP_FL_ACTION_OPCODE_SET_IPV6_SRC      11
 #define NFP_FL_ACTION_OPCODE_SET_IPV6_DST      12
+#define NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL 13
 #define NFP_FL_ACTION_OPCODE_SET_UDP           14
 #define NFP_FL_ACTION_OPCODE_SET_TCP           15
 #define NFP_FL_ACTION_OPCODE_PRE_LAG           16
@@ -82,6 +85,8 @@
 #define NFP_FL_PUSH_VLAN_CFI           BIT(12)
 #define NFP_FL_PUSH_VLAN_VID           GENMASK(11, 0)
 
+#define IPV6_FLOW_LABEL_MASK           cpu_to_be32(0x000fffff)
+
 /* LAG ports */
 #define NFP_FL_LAG_OUT                 0xC0DE0000
 
@@ -125,6 +130,26 @@ struct nfp_fl_set_ip4_addrs {
        __be32 ipv4_dst;
 };
 
+struct nfp_fl_set_ip4_ttl_tos {
+       struct nfp_fl_act_head head;
+       u8 ipv4_ttl_mask;
+       u8 ipv4_tos_mask;
+       u8 ipv4_ttl;
+       u8 ipv4_tos;
+       __be16 reserved;
+};
+
+struct nfp_fl_set_ipv6_tc_hl_fl {
+       struct nfp_fl_act_head head;
+       u8 ipv6_tc_mask;
+       u8 ipv6_hop_limit_mask;
+       __be16 reserved;
+       u8 ipv6_tc;
+       u8 ipv6_hop_limit;
+       __be32 ipv6_label_mask;
+       __be32 ipv6_label;
+};
+
 struct nfp_fl_set_ipv6_addr {
        struct nfp_fl_act_head head;
        __be16 reserved;
@@ -475,6 +500,32 @@ static inline int nfp_flower_cmsg_get_data_len(struct sk_buff *skb)
        return skb->len - NFP_FLOWER_CMSG_HLEN;
 }
 
+static inline bool
+nfp_fl_netdev_is_tunnel_type(struct net_device *netdev,
+                            enum nfp_flower_tun_type tun_type)
+{
+       if (netif_is_vxlan(netdev))
+               return tun_type == NFP_FL_TUNNEL_VXLAN;
+       if (netif_is_geneve(netdev))
+               return tun_type == NFP_FL_TUNNEL_GENEVE;
+
+       return false;
+}
+
+static inline bool nfp_fl_is_netdev_to_offload(struct net_device *netdev)
+{
+       if (!netdev->rtnl_link_ops)
+               return false;
+       if (!strcmp(netdev->rtnl_link_ops->kind, "openvswitch"))
+               return true;
+       if (netif_is_vxlan(netdev))
+               return true;
+       if (netif_is_geneve(netdev))
+               return true;
+
+       return false;
+}
+
 struct sk_buff *
 nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports);
 void
index 81dcf5b..5db838f 100644 (file)
@@ -472,17 +472,25 @@ nfp_fl_lag_schedule_group_remove(struct nfp_fl_lag *lag,
        schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
 }
 
-static int
+static void
 nfp_fl_lag_schedule_group_delete(struct nfp_fl_lag *lag,
                                 struct net_device *master)
 {
        struct nfp_fl_lag_group *group;
+       struct nfp_flower_priv *priv;
+
+       priv = container_of(lag, struct nfp_flower_priv, nfp_lag);
+
+       if (!netif_is_bond_master(master))
+               return;
 
        mutex_lock(&lag->lock);
        group = nfp_fl_lag_find_group_for_master_with_lag(lag, master);
        if (!group) {
                mutex_unlock(&lag->lock);
-               return -ENOENT;
+               nfp_warn(priv->app->cpp, "untracked bond got unregistered %s\n",
+                        netdev_name(master));
+               return;
        }
 
        group->to_remove = true;
@@ -490,7 +498,6 @@ nfp_fl_lag_schedule_group_delete(struct nfp_fl_lag *lag,
        mutex_unlock(&lag->lock);
 
        schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
-       return 0;
 }
 
 static int
@@ -575,7 +582,7 @@ nfp_fl_lag_changeupper_event(struct nfp_fl_lag *lag,
        return 0;
 }
 
-static int
+static void
 nfp_fl_lag_changels_event(struct nfp_fl_lag *lag, struct net_device *netdev,
                          struct netdev_notifier_changelowerstate_info *info)
 {
@@ -586,18 +593,18 @@ nfp_fl_lag_changels_event(struct nfp_fl_lag *lag, struct net_device *netdev,
        unsigned long *flags;
 
        if (!netif_is_lag_port(netdev) || !nfp_netdev_is_nfp_repr(netdev))
-               return 0;
+               return;
 
        lag_lower_info = info->lower_state_info;
        if (!lag_lower_info)
-               return 0;
+               return;
 
        priv = container_of(lag, struct nfp_flower_priv, nfp_lag);
        repr = netdev_priv(netdev);
 
        /* Verify that the repr is associated with this app. */
        if (repr->app != priv->app)
-               return 0;
+               return;
 
        repr_priv = repr->app_priv;
        flags = &repr_priv->lag_port_flags;
@@ -617,20 +624,15 @@ nfp_fl_lag_changels_event(struct nfp_fl_lag *lag, struct net_device *netdev,
        mutex_unlock(&lag->lock);
 
        schedule_delayed_work(&lag->work, NFP_FL_LAG_DELAY);
-       return 0;
 }
 
-static int
-nfp_fl_lag_netdev_event(struct notifier_block *nb, unsigned long event,
-                       void *ptr)
+int nfp_flower_lag_netdev_event(struct nfp_flower_priv *priv,
+                               struct net_device *netdev,
+                               unsigned long event, void *ptr)
 {
-       struct net_device *netdev;
-       struct nfp_fl_lag *lag;
+       struct nfp_fl_lag *lag = &priv->nfp_lag;
        int err;
 
-       netdev = netdev_notifier_info_to_dev(ptr);
-       lag = container_of(nb, struct nfp_fl_lag, lag_nb);
-
        switch (event) {
        case NETDEV_CHANGEUPPER:
                err = nfp_fl_lag_changeupper_event(lag, ptr);
@@ -638,17 +640,11 @@ nfp_fl_lag_netdev_event(struct notifier_block *nb, unsigned long event,
                        return NOTIFY_BAD;
                return NOTIFY_OK;
        case NETDEV_CHANGELOWERSTATE:
-               err = nfp_fl_lag_changels_event(lag, netdev, ptr);
-               if (err)
-                       return NOTIFY_BAD;
+               nfp_fl_lag_changels_event(lag, netdev, ptr);
                return NOTIFY_OK;
        case NETDEV_UNREGISTER:
-               if (netif_is_bond_master(netdev)) {
-                       err = nfp_fl_lag_schedule_group_delete(lag, netdev);
-                       if (err)
-                               return NOTIFY_BAD;
-                       return NOTIFY_OK;
-               }
+               nfp_fl_lag_schedule_group_delete(lag, netdev);
+               return NOTIFY_OK;
        }
 
        return NOTIFY_DONE;
@@ -673,8 +669,6 @@ void nfp_flower_lag_init(struct nfp_fl_lag *lag)
 
        /* 0 is a reserved batch version so increment to first valid value. */
        nfp_fl_increment_version(lag);
-
-       lag->lag_nb.notifier_call = nfp_fl_lag_netdev_event;
 }
 
 void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag)
index 3a54728..5059110 100644 (file)
@@ -146,23 +146,12 @@ nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr)
        return nfp_flower_cmsg_portmod(repr, false, repr->netdev->mtu, false);
 }
 
-static int
-nfp_flower_repr_netdev_init(struct nfp_app *app, struct net_device *netdev)
-{
-       return tc_setup_cb_egdev_register(netdev,
-                                         nfp_flower_setup_tc_egress_cb,
-                                         netdev_priv(netdev));
-}
-
 static void
 nfp_flower_repr_netdev_clean(struct nfp_app *app, struct net_device *netdev)
 {
        struct nfp_repr *repr = netdev_priv(netdev);
 
        kfree(repr->app_priv);
-
-       tc_setup_cb_egdev_unregister(netdev, nfp_flower_setup_tc_egress_cb,
-                                    netdev_priv(netdev));
 }
 
 static void
@@ -568,6 +557,8 @@ static int nfp_flower_init(struct nfp_app *app)
                goto err_cleanup_metadata;
        }
 
+       INIT_LIST_HEAD(&app_priv->indr_block_cb_priv);
+
        return 0;
 
 err_cleanup_metadata:
@@ -661,10 +652,6 @@ static int nfp_flower_start(struct nfp_app *app)
                err = nfp_flower_lag_reset(&app_priv->nfp_lag);
                if (err)
                        return err;
-
-               err = register_netdevice_notifier(&app_priv->nfp_lag.lag_nb);
-               if (err)
-                       return err;
        }
 
        return nfp_tunnel_config_start(app);
@@ -672,12 +659,27 @@ static int nfp_flower_start(struct nfp_app *app)
 
 static void nfp_flower_stop(struct nfp_app *app)
 {
+       nfp_tunnel_config_stop(app);
+}
+
+static int
+nfp_flower_netdev_event(struct nfp_app *app, struct net_device *netdev,
+                       unsigned long event, void *ptr)
+{
        struct nfp_flower_priv *app_priv = app->priv;
+       int ret;
 
-       if (app_priv->flower_ext_feats & NFP_FL_FEATS_LAG)
-               unregister_netdevice_notifier(&app_priv->nfp_lag.lag_nb);
+       if (app_priv->flower_ext_feats & NFP_FL_FEATS_LAG) {
+               ret = nfp_flower_lag_netdev_event(app_priv, netdev, event, ptr);
+               if (ret & NOTIFY_STOP_MASK)
+                       return ret;
+       }
 
-       nfp_tunnel_config_stop(app);
+       ret = nfp_flower_reg_indir_block_handler(app, netdev, event);
+       if (ret & NOTIFY_STOP_MASK)
+               return ret;
+
+       return nfp_tunnel_mac_event_handler(app, netdev, event, ptr);
 }
 
 const struct nfp_app_type app_flower = {
@@ -698,7 +700,6 @@ const struct nfp_app_type app_flower = {
        .vnic_init      = nfp_flower_vnic_init,
        .vnic_clean     = nfp_flower_vnic_clean,
 
-       .repr_init      = nfp_flower_repr_netdev_init,
        .repr_preclean  = nfp_flower_repr_netdev_preclean,
        .repr_clean     = nfp_flower_repr_netdev_clean,
 
@@ -708,6 +709,8 @@ const struct nfp_app_type app_flower = {
        .start          = nfp_flower_start,
        .stop           = nfp_flower_stop,
 
+       .netdev_event   = nfp_flower_netdev_event,
+
        .ctrl_msg_rx    = nfp_flower_cmsg_rx,
 
        .sriov_enable   = nfp_flower_sriov_enable,
index 90045ba..b858bac 100644 (file)
@@ -20,7 +20,6 @@ struct nfp_fl_pre_lag;
 struct net_device;
 struct nfp_app;
 
-#define NFP_FL_STATS_CTX_DONT_CARE     cpu_to_be32(0xffffffff)
 #define NFP_FL_STATS_ELEM_RS           FIELD_SIZEOF(struct nfp_fl_stats_id, \
                                                     init_unalloc)
 #define NFP_FLOWER_MASK_ENTRY_RS       256
@@ -72,7 +71,6 @@ struct nfp_mtu_conf {
 
 /**
  * struct nfp_fl_lag - Flower APP priv data for link aggregation
- * @lag_nb:            Notifier to track master/slave events
  * @work:              Work queue for writing configs to the HW
  * @lock:              Lock to protect lag_group_list
  * @group_list:                List of all master/slave groups offloaded
@@ -85,7 +83,6 @@ struct nfp_mtu_conf {
  *                     retransmission
  */
 struct nfp_fl_lag {
-       struct notifier_block lag_nb;
        struct delayed_work work;
        struct mutex lock;
        struct list_head group_list;
@@ -126,13 +123,13 @@ struct nfp_fl_lag {
  * @nfp_neigh_off_lock:        Lock for the neighbour address list
  * @nfp_mac_off_ids:   IDA to manage id assignment for offloaded macs
  * @nfp_mac_off_count: Number of MACs in address list
- * @nfp_tun_mac_nb:    Notifier to monitor link state
  * @nfp_tun_neigh_nb:  Notifier to monitor neighbour state
  * @reify_replies:     atomically stores the number of replies received
  *                     from firmware for repr reify
  * @reify_wait_queue:  wait queue for repr reify response counting
  * @mtu_conf:          Configuration of repr MTU value
  * @nfp_lag:           Link aggregation data block
+ * @indr_block_cb_priv:        List of priv data passed to indirect block cbs
  */
 struct nfp_flower_priv {
        struct nfp_app *app;
@@ -160,12 +157,12 @@ struct nfp_flower_priv {
        spinlock_t nfp_neigh_off_lock;
        struct ida nfp_mac_off_ids;
        int nfp_mac_off_count;
-       struct notifier_block nfp_tun_mac_nb;
        struct notifier_block nfp_tun_neigh_nb;
        atomic_t reify_replies;
        wait_queue_head_t reify_wait_queue;
        struct nfp_mtu_conf mtu_conf;
        struct nfp_fl_lag nfp_lag;
+       struct list_head indr_block_cb_priv;
 };
 
 /**
@@ -209,7 +206,6 @@ struct nfp_fl_payload {
        char *unmasked_data;
        char *mask_data;
        char *action_data;
-       bool ingress_offload;
 };
 
 extern const struct rhashtable_params nfp_flower_table_params;
@@ -226,7 +222,8 @@ void nfp_flower_metadata_cleanup(struct nfp_app *app);
 
 int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev,
                        enum tc_setup_type type, void *type_data);
-int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,
+int nfp_flower_compile_flow_match(struct nfp_app *app,
+                                 struct tc_cls_flower_offload *flow,
                                  struct nfp_fl_key_ls *key_ls,
                                  struct net_device *netdev,
                                  struct nfp_fl_payload *nfp_flow,
@@ -244,7 +241,7 @@ int nfp_modify_flow_metadata(struct nfp_app *app,
 
 struct nfp_fl_payload *
 nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie,
-                          struct net_device *netdev, __be32 host_ctx);
+                          struct net_device *netdev);
 struct nfp_fl_payload *
 nfp_flower_remove_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie);
 
@@ -252,21 +249,28 @@ void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb);
 
 int nfp_tunnel_config_start(struct nfp_app *app);
 void nfp_tunnel_config_stop(struct nfp_app *app);
+int nfp_tunnel_mac_event_handler(struct nfp_app *app,
+                                struct net_device *netdev,
+                                unsigned long event, void *ptr);
 void nfp_tunnel_write_macs(struct nfp_app *app);
 void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4);
 void nfp_tunnel_add_ipv4_off(struct nfp_app *app, __be32 ipv4);
 void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb);
 void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb);
-int nfp_flower_setup_tc_egress_cb(enum tc_setup_type type, void *type_data,
-                                 void *cb_priv);
 void nfp_flower_lag_init(struct nfp_fl_lag *lag);
 void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag);
 int nfp_flower_lag_reset(struct nfp_fl_lag *lag);
+int nfp_flower_lag_netdev_event(struct nfp_flower_priv *priv,
+                               struct net_device *netdev,
+                               unsigned long event, void *ptr);
 bool nfp_flower_lag_unprocessed_msg(struct nfp_app *app, struct sk_buff *skb);
 int nfp_flower_lag_populate_pre_action(struct nfp_app *app,
                                       struct net_device *master,
                                       struct nfp_fl_pre_lag *pre_act);
 int nfp_flower_lag_get_output_id(struct nfp_app *app,
                                 struct net_device *master);
+int nfp_flower_reg_indir_block_handler(struct nfp_app *app,
+                                      struct net_device *netdev,
+                                      unsigned long event);
 
 #endif
index e54fb60..cdf7559 100644 (file)
@@ -52,10 +52,13 @@ nfp_flower_compile_port(struct nfp_flower_in_port *frame, u32 cmsg_port,
                return 0;
        }
 
-       if (tun_type)
+       if (tun_type) {
                frame->in_port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
-       else
+       } else {
+               if (!cmsg_port)
+                       return -EOPNOTSUPP;
                frame->in_port = cpu_to_be32(cmsg_port);
+       }
 
        return 0;
 }
@@ -289,17 +292,21 @@ nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *frame,
        }
 }
 
-int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,
+int nfp_flower_compile_flow_match(struct nfp_app *app,
+                                 struct tc_cls_flower_offload *flow,
                                  struct nfp_fl_key_ls *key_ls,
                                  struct net_device *netdev,
                                  struct nfp_fl_payload *nfp_flow,
                                  enum nfp_flower_tun_type tun_type)
 {
-       struct nfp_repr *netdev_repr;
+       u32 cmsg_port = 0;
        int err;
        u8 *ext;
        u8 *msk;
 
+       if (nfp_netdev_is_nfp_repr(netdev))
+               cmsg_port = nfp_repr_get_port_id(netdev);
+
        memset(nfp_flow->unmasked_data, 0, key_ls->key_size);
        memset(nfp_flow->mask_data, 0, key_ls->key_size);
 
@@ -327,15 +334,13 @@ int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,
 
        /* Populate Exact Port data. */
        err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext,
-                                     nfp_repr_get_port_id(netdev),
-                                     false, tun_type);
+                                     cmsg_port, false, tun_type);
        if (err)
                return err;
 
        /* Populate Mask Port Data. */
        err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk,
-                                     nfp_repr_get_port_id(netdev),
-                                     true, tun_type);
+                                     cmsg_port, true, tun_type);
        if (err)
                return err;
 
@@ -399,16 +404,13 @@ int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow,
                msk += sizeof(struct nfp_flower_ipv4_udp_tun);
 
                /* Configure tunnel end point MAC. */
-               if (nfp_netdev_is_nfp_repr(netdev)) {
-                       netdev_repr = netdev_priv(netdev);
-                       nfp_tunnel_write_macs(netdev_repr->app);
-
-                       /* Store the tunnel destination in the rule data.
-                        * This must be present and be an exact match.
-                        */
-                       nfp_flow->nfp_tun_ipv4_addr = tun_dst;
-                       nfp_tunnel_add_ipv4_off(netdev_repr->app, tun_dst);
-               }
+               nfp_tunnel_write_macs(app);
+
+               /* Store the tunnel destination in the rule data.
+                * This must be present and be an exact match.
+                */
+               nfp_flow->nfp_tun_ipv4_addr = tun_dst;
+               nfp_tunnel_add_ipv4_off(app, tun_dst);
 
                if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) {
                        err = nfp_flower_compile_geneve_opt(ext, flow, false);
index 48729bf..573a440 100644 (file)
@@ -21,7 +21,6 @@ struct nfp_mask_id_table {
 struct nfp_fl_flow_table_cmp_arg {
        struct net_device *netdev;
        unsigned long cookie;
-       __be32 host_ctx;
 };
 
 static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id)
@@ -76,14 +75,13 @@ static int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id)
 /* Must be called with either RTNL or rcu_read_lock */
 struct nfp_fl_payload *
 nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie,
-                          struct net_device *netdev, __be32 host_ctx)
+                          struct net_device *netdev)
 {
        struct nfp_fl_flow_table_cmp_arg flower_cmp_arg;
        struct nfp_flower_priv *priv = app->priv;
 
        flower_cmp_arg.netdev = netdev;
        flower_cmp_arg.cookie = tc_flower_cookie;
-       flower_cmp_arg.host_ctx = host_ctx;
 
        return rhashtable_lookup_fast(&priv->flow_table, &flower_cmp_arg,
                                      nfp_flower_table_params);
@@ -287,6 +285,7 @@ int nfp_compile_flow_metadata(struct nfp_app *app,
 
        nfp_flow->meta.host_ctx_id = cpu_to_be32(stats_cxt);
        nfp_flow->meta.host_cookie = cpu_to_be64(flow->cookie);
+       nfp_flow->ingress_dev = netdev;
 
        new_mask_id = 0;
        if (!nfp_check_mask_add(app, nfp_flow->mask_data,
@@ -306,8 +305,7 @@ int nfp_compile_flow_metadata(struct nfp_app *app,
        priv->stats[stats_cxt].bytes = 0;
        priv->stats[stats_cxt].used = jiffies;
 
-       check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev,
-                                                NFP_FL_STATS_CTX_DONT_CARE);
+       check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev);
        if (check_entry) {
                if (nfp_release_stats_entry(app, stats_cxt))
                        return -EINVAL;
@@ -352,9 +350,7 @@ static int nfp_fl_obj_cmpfn(struct rhashtable_compare_arg *arg,
        const struct nfp_fl_flow_table_cmp_arg *cmp_arg = arg->key;
        const struct nfp_fl_payload *flow_entry = obj;
 
-       if ((!cmp_arg->netdev || flow_entry->ingress_dev == cmp_arg->netdev) &&
-           (cmp_arg->host_ctx == NFP_FL_STATS_CTX_DONT_CARE ||
-            flow_entry->meta.host_ctx_id == cmp_arg->host_ctx))
+       if (flow_entry->ingress_dev == cmp_arg->netdev)
                return flow_entry->tc_flower_cookie != cmp_arg->cookie;
 
        return 1;
index 2f49eb7..21499a5 100644 (file)
         BIT(FLOW_DISSECTOR_KEY_ENC_PORTS))
 
 static int
-nfp_flower_xmit_flow(struct net_device *netdev,
-                    struct nfp_fl_payload *nfp_flow, u8 mtype)
+nfp_flower_xmit_flow(struct nfp_app *app, struct nfp_fl_payload *nfp_flow,
+                    u8 mtype)
 {
        u32 meta_len, key_len, mask_len, act_len, tot_len;
-       struct nfp_repr *priv = netdev_priv(netdev);
        struct sk_buff *skb;
        unsigned char *msg;
 
@@ -78,7 +77,7 @@ nfp_flower_xmit_flow(struct net_device *netdev,
        nfp_flow->meta.mask_len >>= NFP_FL_LW_SIZ;
        nfp_flow->meta.act_len >>= NFP_FL_LW_SIZ;
 
-       skb = nfp_flower_cmsg_alloc(priv->app, tot_len, mtype, GFP_KERNEL);
+       skb = nfp_flower_cmsg_alloc(app, tot_len, mtype, GFP_KERNEL);
        if (!skb)
                return -ENOMEM;
 
@@ -96,7 +95,7 @@ nfp_flower_xmit_flow(struct net_device *netdev,
        nfp_flow->meta.mask_len <<= NFP_FL_LW_SIZ;
        nfp_flow->meta.act_len <<= NFP_FL_LW_SIZ;
 
-       nfp_ctrl_tx(priv->app->ctrl, skb);
+       nfp_ctrl_tx(app->ctrl, skb);
 
        return 0;
 }
@@ -129,9 +128,9 @@ nfp_flower_calc_opt_layer(struct flow_dissector_key_enc_opts *enc_opts,
 
 static int
 nfp_flower_calculate_key_layers(struct nfp_app *app,
+                               struct net_device *netdev,
                                struct nfp_fl_key_ls *ret_key_ls,
                                struct tc_cls_flower_offload *flow,
-                               bool egress,
                                enum nfp_flower_tun_type *tun_type)
 {
        struct flow_dissector_key_basic *mask_basic = NULL;
@@ -187,8 +186,6 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                        skb_flow_dissector_target(flow->dissector,
                                                  FLOW_DISSECTOR_KEY_ENC_CONTROL,
                                                  flow->key);
-               if (!egress)
-                       return -EOPNOTSUPP;
 
                if (mask_enc_ctl->addr_type != 0xffff ||
                    enc_ctl->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS)
@@ -251,9 +248,10 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
                default:
                        return -EOPNOTSUPP;
                }
-       } else if (egress) {
-               /* Reject non tunnel matches offloaded to egress repr. */
-               return -EOPNOTSUPP;
+
+               /* Ensure the ingress netdev matches the expected tun type. */
+               if (!nfp_fl_netdev_is_tunnel_type(netdev, *tun_type))
+                       return -EOPNOTSUPP;
        }
 
        if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
@@ -374,7 +372,7 @@ nfp_flower_calculate_key_layers(struct nfp_app *app,
 }
 
 static struct nfp_fl_payload *
-nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer, bool egress)
+nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer)
 {
        struct nfp_fl_payload *flow_pay;
 
@@ -398,7 +396,6 @@ nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer, bool egress)
 
        flow_pay->nfp_tun_ipv4_addr = 0;
        flow_pay->meta.flags = 0;
-       flow_pay->ingress_offload = !egress;
 
        return flow_pay;
 
@@ -416,7 +413,6 @@ err_free_flow:
  * @app:       Pointer to the APP handle
  * @netdev:    netdev structure.
  * @flow:      TC flower classifier offload structure.
- * @egress:    NFP netdev is the egress.
  *
  * Adds a new flow to the repeated hash structure and action payload.
  *
@@ -424,46 +420,35 @@ err_free_flow:
  */
 static int
 nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
-                      struct tc_cls_flower_offload *flow, bool egress)
+                      struct tc_cls_flower_offload *flow)
 {
        enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE;
-       struct nfp_port *port = nfp_port_from_netdev(netdev);
        struct nfp_flower_priv *priv = app->priv;
        struct nfp_fl_payload *flow_pay;
        struct nfp_fl_key_ls *key_layer;
-       struct net_device *ingr_dev;
+       struct nfp_port *port = NULL;
        int err;
 
-       ingr_dev = egress ? NULL : netdev;
-       flow_pay = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev,
-                                             NFP_FL_STATS_CTX_DONT_CARE);
-       if (flow_pay) {
-               /* Ignore as duplicate if it has been added by different cb. */
-               if (flow_pay->ingress_offload && egress)
-                       return 0;
-               else
-                       return -EOPNOTSUPP;
-       }
+       if (nfp_netdev_is_nfp_repr(netdev))
+               port = nfp_port_from_netdev(netdev);
 
        key_layer = kmalloc(sizeof(*key_layer), GFP_KERNEL);
        if (!key_layer)
                return -ENOMEM;
 
-       err = nfp_flower_calculate_key_layers(app, key_layer, flow, egress,
+       err = nfp_flower_calculate_key_layers(app, netdev, key_layer, flow,
                                              &tun_type);
        if (err)
                goto err_free_key_ls;
 
-       flow_pay = nfp_flower_allocate_new(key_layer, egress);
+       flow_pay = nfp_flower_allocate_new(key_layer);
        if (!flow_pay) {
                err = -ENOMEM;
                goto err_free_key_ls;
        }
 
-       flow_pay->ingress_dev = egress ? NULL : netdev;
-
-       err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay,
-                                           tun_type);
+       err = nfp_flower_compile_flow_match(app, flow, key_layer, netdev,
+                                           flow_pay, tun_type);
        if (err)
                goto err_destroy_flow;
 
@@ -471,8 +456,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
        if (err)
                goto err_destroy_flow;
 
-       err = nfp_compile_flow_metadata(app, flow, flow_pay,
-                                       flow_pay->ingress_dev);
+       err = nfp_compile_flow_metadata(app, flow, flow_pay, netdev);
        if (err)
                goto err_destroy_flow;
 
@@ -482,12 +466,13 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
        if (err)
                goto err_release_metadata;
 
-       err = nfp_flower_xmit_flow(netdev, flow_pay,
+       err = nfp_flower_xmit_flow(app, flow_pay,
                                   NFP_FLOWER_CMSG_TYPE_FLOW_ADD);
        if (err)
                goto err_remove_rhash;
 
-       port->tc_offload_cnt++;
+       if (port)
+               port->tc_offload_cnt++;
 
        /* Deallocate flow payload when flower rule has been destroyed. */
        kfree(key_layer);
@@ -515,7 +500,6 @@ err_free_key_ls:
  * @app:       Pointer to the APP handle
  * @netdev:    netdev structure.
  * @flow:      TC flower classifier offload structure
- * @egress:    Netdev is the egress dev.
  *
  * Removes a flow from the repeated hash structure and clears the
  * action payload.
@@ -524,19 +508,19 @@ err_free_key_ls:
  */
 static int
 nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,
-                      struct tc_cls_flower_offload *flow, bool egress)
+                      struct tc_cls_flower_offload *flow)
 {
-       struct nfp_port *port = nfp_port_from_netdev(netdev);
        struct nfp_flower_priv *priv = app->priv;
        struct nfp_fl_payload *nfp_flow;
-       struct net_device *ingr_dev;
+       struct nfp_port *port = NULL;
        int err;
 
-       ingr_dev = egress ? NULL : netdev;
-       nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev,
-                                             NFP_FL_STATS_CTX_DONT_CARE);
+       if (nfp_netdev_is_nfp_repr(netdev))
+               port = nfp_port_from_netdev(netdev);
+
+       nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, netdev);
        if (!nfp_flow)
-               return egress ? 0 : -ENOENT;
+               return -ENOENT;
 
        err = nfp_modify_flow_metadata(app, nfp_flow);
        if (err)
@@ -545,13 +529,14 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,
        if (nfp_flow->nfp_tun_ipv4_addr)
                nfp_tunnel_del_ipv4_off(app, nfp_flow->nfp_tun_ipv4_addr);
 
-       err = nfp_flower_xmit_flow(netdev, nfp_flow,
+       err = nfp_flower_xmit_flow(app, nfp_flow,
                                   NFP_FLOWER_CMSG_TYPE_FLOW_DEL);
        if (err)
                goto err_free_flow;
 
 err_free_flow:
-       port->tc_offload_cnt--;
+       if (port)
+               port->tc_offload_cnt--;
        kfree(nfp_flow->action_data);
        kfree(nfp_flow->mask_data);
        kfree(nfp_flow->unmasked_data);
@@ -567,7 +552,6 @@ err_free_flow:
  * @app:       Pointer to the APP handle
  * @netdev:    Netdev structure.
  * @flow:      TC flower classifier offload structure
- * @egress:    Netdev is the egress dev.
  *
  * Populates a flow statistics structure which which corresponds to a
  * specific flow.
@@ -576,22 +560,16 @@ err_free_flow:
  */
 static int
 nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev,
-                    struct tc_cls_flower_offload *flow, bool egress)
+                    struct tc_cls_flower_offload *flow)
 {
        struct nfp_flower_priv *priv = app->priv;
        struct nfp_fl_payload *nfp_flow;
-       struct net_device *ingr_dev;
        u32 ctx_id;
 
-       ingr_dev = egress ? NULL : netdev;
-       nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev,
-                                             NFP_FL_STATS_CTX_DONT_CARE);
+       nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, netdev);
        if (!nfp_flow)
                return -EINVAL;
 
-       if (nfp_flow->ingress_offload && egress)
-               return 0;
-
        ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
 
        spin_lock_bh(&priv->stats_lock);
@@ -608,35 +586,18 @@ nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev,
 
 static int
 nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev,
-                       struct tc_cls_flower_offload *flower, bool egress)
+                       struct tc_cls_flower_offload *flower)
 {
        if (!eth_proto_is_802_3(flower->common.protocol))
                return -EOPNOTSUPP;
 
        switch (flower->command) {
        case TC_CLSFLOWER_REPLACE:
-               return nfp_flower_add_offload(app, netdev, flower, egress);
+               return nfp_flower_add_offload(app, netdev, flower);
        case TC_CLSFLOWER_DESTROY:
-               return nfp_flower_del_offload(app, netdev, flower, egress);
+               return nfp_flower_del_offload(app, netdev, flower);
        case TC_CLSFLOWER_STATS:
-               return nfp_flower_get_stats(app, netdev, flower, egress);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-int nfp_flower_setup_tc_egress_cb(enum tc_setup_type type, void *type_data,
-                                 void *cb_priv)
-{
-       struct nfp_repr *repr = cb_priv;
-
-       if (!tc_cls_can_offload_and_chain0(repr->netdev, type_data))
-               return -EOPNOTSUPP;
-
-       switch (type) {
-       case TC_SETUP_CLSFLOWER:
-               return nfp_flower_repr_offload(repr->app, repr->netdev,
-                                              type_data, true);
+               return nfp_flower_get_stats(app, netdev, flower);
        default:
                return -EOPNOTSUPP;
        }
@@ -653,7 +614,7 @@ static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type,
        switch (type) {
        case TC_SETUP_CLSFLOWER:
                return nfp_flower_repr_offload(repr->app, repr->netdev,
-                                              type_data, false);
+                                              type_data);
        default:
                return -EOPNOTSUPP;
        }
@@ -692,3 +653,129 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev,
                return -EOPNOTSUPP;
        }
 }
+
+struct nfp_flower_indr_block_cb_priv {
+       struct net_device *netdev;
+       struct nfp_app *app;
+       struct list_head list;
+};
+
+static struct nfp_flower_indr_block_cb_priv *
+nfp_flower_indr_block_cb_priv_lookup(struct nfp_app *app,
+                                    struct net_device *netdev)
+{
+       struct nfp_flower_indr_block_cb_priv *cb_priv;
+       struct nfp_flower_priv *priv = app->priv;
+
+       /* All callback list access should be protected by RTNL. */
+       ASSERT_RTNL();
+
+       list_for_each_entry(cb_priv, &priv->indr_block_cb_priv, list)
+               if (cb_priv->netdev == netdev)
+                       return cb_priv;
+
+       return NULL;
+}
+
+static int nfp_flower_setup_indr_block_cb(enum tc_setup_type type,
+                                         void *type_data, void *cb_priv)
+{
+       struct nfp_flower_indr_block_cb_priv *priv = cb_priv;
+       struct tc_cls_flower_offload *flower = type_data;
+
+       if (flower->common.chain_index)
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return nfp_flower_repr_offload(priv->app, priv->netdev,
+                                              type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app,
+                              struct tc_block_offload *f)
+{
+       struct nfp_flower_indr_block_cb_priv *cb_priv;
+       struct nfp_flower_priv *priv = app->priv;
+       int err;
+
+       if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       switch (f->command) {
+       case TC_BLOCK_BIND:
+               cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL);
+               if (!cb_priv)
+                       return -ENOMEM;
+
+               cb_priv->netdev = netdev;
+               cb_priv->app = app;
+               list_add(&cb_priv->list, &priv->indr_block_cb_priv);
+
+               err = tcf_block_cb_register(f->block,
+                                           nfp_flower_setup_indr_block_cb,
+                                           netdev, cb_priv, f->extack);
+               if (err) {
+                       list_del(&cb_priv->list);
+                       kfree(cb_priv);
+               }
+
+               return err;
+       case TC_BLOCK_UNBIND:
+               tcf_block_cb_unregister(f->block,
+                                       nfp_flower_setup_indr_block_cb, netdev);
+               cb_priv = nfp_flower_indr_block_cb_priv_lookup(app, netdev);
+               if (cb_priv) {
+                       list_del(&cb_priv->list);
+                       kfree(cb_priv);
+               }
+
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int
+nfp_flower_indr_setup_tc_cb(struct net_device *netdev, void *cb_priv,
+                           enum tc_setup_type type, void *type_data)
+{
+       switch (type) {
+       case TC_SETUP_BLOCK:
+               return nfp_flower_setup_indr_tc_block(netdev, cb_priv,
+                                                     type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+int nfp_flower_reg_indir_block_handler(struct nfp_app *app,
+                                      struct net_device *netdev,
+                                      unsigned long event)
+{
+       int err;
+
+       if (!nfp_fl_is_netdev_to_offload(netdev))
+               return NOTIFY_OK;
+
+       if (event == NETDEV_REGISTER) {
+               err = __tc_indr_block_cb_register(netdev, app,
+                                                 nfp_flower_indr_setup_tc_cb,
+                                                 netdev);
+               if (err)
+                       nfp_flower_cmsg_warn(app,
+                                            "Indirect block reg failed - %s\n",
+                                            netdev->name);
+       } else if (event == NETDEV_UNREGISTER) {
+               __tc_indr_block_cb_unregister(netdev,
+                                             nfp_flower_indr_setup_tc_cb,
+                                             netdev);
+       }
+
+       return NOTIFY_OK;
+}
index 8e5bec0..2d9f26a 100644 (file)
@@ -4,7 +4,6 @@
 #include <linux/etherdevice.h>
 #include <linux/inetdevice.h>
 #include <net/netevent.h>
-#include <net/vxlan.h>
 #include <linux/idr.h>
 #include <net/dst_metadata.h>
 #include <net/arp.h>
@@ -182,18 +181,6 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
        }
 }
 
-static bool nfp_tun_is_netdev_to_offload(struct net_device *netdev)
-{
-       if (!netdev->rtnl_link_ops)
-               return false;
-       if (!strcmp(netdev->rtnl_link_ops->kind, "openvswitch"))
-               return true;
-       if (netif_is_vxlan(netdev))
-               return true;
-
-       return false;
-}
-
 static int
 nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata,
                         gfp_t flag)
@@ -615,7 +602,7 @@ static void nfp_tun_add_to_mac_offload_list(struct net_device *netdev,
 
        if (nfp_netdev_is_nfp_repr(netdev))
                port = nfp_repr_get_port_id(netdev);
-       else if (!nfp_tun_is_netdev_to_offload(netdev))
+       else if (!nfp_fl_is_netdev_to_offload(netdev))
                return;
 
        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
@@ -652,29 +639,16 @@ static void nfp_tun_add_to_mac_offload_list(struct net_device *netdev,
        mutex_unlock(&priv->nfp_mac_off_lock);
 }
 
-static int nfp_tun_mac_event_handler(struct notifier_block *nb,
-                                    unsigned long event, void *ptr)
+int nfp_tunnel_mac_event_handler(struct nfp_app *app,
+                                struct net_device *netdev,
+                                unsigned long event, void *ptr)
 {
-       struct nfp_flower_priv *app_priv;
-       struct net_device *netdev;
-       struct nfp_app *app;
-
        if (event == NETDEV_DOWN || event == NETDEV_UNREGISTER) {
-               app_priv = container_of(nb, struct nfp_flower_priv,
-                                       nfp_tun_mac_nb);
-               app = app_priv->app;
-               netdev = netdev_notifier_info_to_dev(ptr);
-
                /* If non-nfp netdev then free its offload index. */
-               if (nfp_tun_is_netdev_to_offload(netdev))
+               if (nfp_fl_is_netdev_to_offload(netdev))
                        nfp_tun_del_mac_idx(app, netdev->ifindex);
        } else if (event == NETDEV_UP || event == NETDEV_CHANGEADDR ||
                   event == NETDEV_REGISTER) {
-               app_priv = container_of(nb, struct nfp_flower_priv,
-                                       nfp_tun_mac_nb);
-               app = app_priv->app;
-               netdev = netdev_notifier_info_to_dev(ptr);
-
                nfp_tun_add_to_mac_offload_list(netdev, app);
 
                /* Force a list write to keep NFP up to date. */
@@ -686,14 +660,11 @@ static int nfp_tun_mac_event_handler(struct notifier_block *nb,
 int nfp_tunnel_config_start(struct nfp_app *app)
 {
        struct nfp_flower_priv *priv = app->priv;
-       struct net_device *netdev;
-       int err;
 
        /* Initialise priv data for MAC offloading. */
        priv->nfp_mac_off_count = 0;
        mutex_init(&priv->nfp_mac_off_lock);
        INIT_LIST_HEAD(&priv->nfp_mac_off_list);
-       priv->nfp_tun_mac_nb.notifier_call = nfp_tun_mac_event_handler;
        mutex_init(&priv->nfp_mac_index_lock);
        INIT_LIST_HEAD(&priv->nfp_mac_index_list);
        ida_init(&priv->nfp_mac_off_ids);
@@ -707,27 +678,7 @@ int nfp_tunnel_config_start(struct nfp_app *app)
        INIT_LIST_HEAD(&priv->nfp_neigh_off_list);
        priv->nfp_tun_neigh_nb.notifier_call = nfp_tun_neigh_event_handler;
 
-       err = register_netdevice_notifier(&priv->nfp_tun_mac_nb);
-       if (err)
-               goto err_free_mac_ida;
-
-       err = register_netevent_notifier(&priv->nfp_tun_neigh_nb);
-       if (err)
-               goto err_unreg_mac_nb;
-
-       /* Parse netdevs already registered for MACs that need offloaded. */
-       rtnl_lock();
-       for_each_netdev(&init_net, netdev)
-               nfp_tun_add_to_mac_offload_list(netdev, app);
-       rtnl_unlock();
-
-       return 0;
-
-err_unreg_mac_nb:
-       unregister_netdevice_notifier(&priv->nfp_tun_mac_nb);
-err_free_mac_ida:
-       ida_destroy(&priv->nfp_mac_off_ids);
-       return err;
+       return register_netevent_notifier(&priv->nfp_tun_neigh_nb);
 }
 
 void nfp_tunnel_config_stop(struct nfp_app *app)
@@ -739,7 +690,6 @@ void nfp_tunnel_config_stop(struct nfp_app *app)
        struct nfp_ipv4_addr_entry *ip_entry;
        struct list_head *ptr, *storage;
 
-       unregister_netdevice_notifier(&priv->nfp_tun_mac_nb);
        unregister_netevent_notifier(&priv->nfp_tun_neigh_nb);
 
        /* Free any memory that may be occupied by MAC list. */
index 68a0991..3a97328 100644 (file)
@@ -131,11 +131,100 @@ nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type,
        struct nfp_reprs *old;
 
        old = nfp_reprs_get_locked(app, type);
+       rtnl_lock();
        rcu_assign_pointer(app->reprs[type], reprs);
+       rtnl_unlock();
 
        return old;
 }
 
+static void
+nfp_app_netdev_feat_change(struct nfp_app *app, struct net_device *netdev)
+{
+       struct nfp_net *nn;
+       unsigned int type;
+
+       if (!nfp_netdev_is_nfp_net(netdev))
+               return;
+       nn = netdev_priv(netdev);
+       if (nn->app != app)
+               return;
+
+       for (type = 0; type < __NFP_REPR_TYPE_MAX; type++) {
+               struct nfp_reprs *reprs;
+               unsigned int i;
+
+               reprs = rtnl_dereference(app->reprs[type]);
+               if (!reprs)
+                       continue;
+
+               for (i = 0; i < reprs->num_reprs; i++) {
+                       struct net_device *repr;
+
+                       repr = rtnl_dereference(reprs->reprs[i]);
+                       if (!repr)
+                               continue;
+
+                       nfp_repr_transfer_features(repr, netdev);
+               }
+       }
+}
+
+static int
+nfp_app_netdev_event(struct notifier_block *nb, unsigned long event, void *ptr)
+{
+       struct net_device *netdev;
+       struct nfp_app *app;
+
+       netdev = netdev_notifier_info_to_dev(ptr);
+       app = container_of(nb, struct nfp_app, netdev_nb);
+
+       /* Handle events common code is interested in */
+       switch (event) {
+       case NETDEV_FEAT_CHANGE:
+               nfp_app_netdev_feat_change(app, netdev);
+               break;
+       }
+
+       /* Call offload specific handlers */
+       if (app->type->netdev_event)
+               return app->type->netdev_event(app, netdev, event, ptr);
+       return NOTIFY_DONE;
+}
+
+int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl)
+{
+       int err;
+
+       app->ctrl = ctrl;
+
+       if (app->type->start) {
+               err = app->type->start(app);
+               if (err)
+                       return err;
+       }
+
+       app->netdev_nb.notifier_call = nfp_app_netdev_event;
+       err = register_netdevice_notifier(&app->netdev_nb);
+       if (err)
+               goto err_app_stop;
+
+       return 0;
+
+err_app_stop:
+       if (app->type->stop)
+               app->type->stop(app);
+       return err;
+}
+
+void nfp_app_stop(struct nfp_app *app)
+{
+       unregister_netdevice_notifier(&app->netdev_nb);
+
+       if (app->type->stop)
+               app->type->stop(app);
+}
+
 struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id)
 {
        struct nfp_app *app;
index 4d6ecf9..d578d85 100644 (file)
@@ -69,6 +69,7 @@ extern const struct nfp_app_type app_abm;
  * @port_get_stats_strings:    get strings for extra statistics
  * @start:     start application logic
  * @stop:      stop application logic
+ * @netdev_event:      Netdevice notifier event
  * @ctrl_msg_rx:    control message handler
  * @ctrl_msg_rx_raw:   handler for control messages from data queues
  * @setup_tc:  setup TC ndo
@@ -122,6 +123,9 @@ struct nfp_app_type {
        int (*start)(struct nfp_app *app);
        void (*stop)(struct nfp_app *app);
 
+       int (*netdev_event)(struct nfp_app *app, struct net_device *netdev,
+                           unsigned long event, void *ptr);
+
        void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb);
        void (*ctrl_msg_rx_raw)(struct nfp_app *app, const void *data,
                                unsigned int len);
@@ -151,6 +155,7 @@ struct nfp_app_type {
  * @reprs:     array of pointers to representors
  * @type:      pointer to const application ops and info
  * @ctrl_mtu:  MTU to set on the control vNIC (set in .init())
+ * @netdev_nb: Netdevice notifier block
  * @priv:      app-specific priv data
  */
 struct nfp_app {
@@ -163,6 +168,9 @@ struct nfp_app {
 
        const struct nfp_app_type *type;
        unsigned int ctrl_mtu;
+
+       struct notifier_block netdev_nb;
+
        void *priv;
 };
 
@@ -264,21 +272,6 @@ nfp_app_repr_change_mtu(struct nfp_app *app, struct net_device *netdev,
        return app->type->repr_change_mtu(app, netdev, new_mtu);
 }
 
-static inline int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl)
-{
-       app->ctrl = ctrl;
-       if (!app->type->start)
-               return 0;
-       return app->type->start(app);
-}
-
-static inline void nfp_app_stop(struct nfp_app *app)
-{
-       if (!app->type->stop)
-               return;
-       app->type->stop(app);
-}
-
 static inline const char *nfp_app_name(struct nfp_app *app)
 {
        if (!app)
@@ -430,6 +423,8 @@ nfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority);
 
 struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id);
 void nfp_app_free(struct nfp_app *app);
+int nfp_app_start(struct nfp_app *app, struct nfp_net *ctrl);
+void nfp_app_stop(struct nfp_app *app);
 
 /* Callbacks shared between apps */
 
index 6f0c37d..be37c2d 100644 (file)
@@ -158,6 +158,7 @@ struct nfp_net_tx_desc {
                        __le16 data_len; /* Length of frame + meta data */
                } __packed;
                __le32 vals[4];
+               __le64 vals8[2];
        };
 };
 
@@ -543,6 +544,7 @@ struct nfp_net_dp {
  * @reconfig_timer_active:  Timer for reading reconfiguration results is pending
  * @reconfig_sync_present:  Some thread is performing synchronous reconfig
  * @reconfig_timer:    Timer for async reading of reconfig results
+ * @reconfig_in_progress_update:       Update FW is processing now (debug only)
  * @link_up:            Is the link up?
  * @link_status_lock:  Protects @link_* and ensures atomicity with BAR reading
  * @rx_coalesce_usecs:      RX interrupt moderation usecs delay parameter
@@ -611,6 +613,7 @@ struct nfp_net {
        bool reconfig_timer_active;
        bool reconfig_sync_present;
        struct timer_list reconfig_timer;
+       u32 reconfig_in_progress_update;
 
        u32 rx_coalesce_usecs;
        u32 rx_coalesce_max_frames;
@@ -851,7 +854,7 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
                            void __iomem *ctrl_bar);
 
 struct nfp_net *
-nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
+nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
              unsigned int max_tx_rings, unsigned int max_rx_rings);
 void nfp_net_free(struct nfp_net *nn);
 
@@ -868,6 +871,7 @@ unsigned int nfp_net_rss_key_sz(struct nfp_net *nn);
 void nfp_net_rss_write_itbl(struct nfp_net *nn);
 void nfp_net_rss_write_key(struct nfp_net *nn);
 void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
+int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd);
 
 unsigned int
 nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,
index 6bddfcf..e97636d 100644 (file)
@@ -101,6 +101,7 @@ static void nfp_net_reconfig_start(struct nfp_net *nn, u32 update)
        /* ensure update is written before pinging HW */
        nn_pci_flush(nn);
        nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
+       nn->reconfig_in_progress_update = update;
 }
 
 /* Pass 0 as update to run posted reconfigs. */
@@ -123,10 +124,14 @@ static bool nfp_net_reconfig_check_done(struct nfp_net *nn, bool last_check)
        if (reg == 0)
                return true;
        if (reg & NFP_NET_CFG_UPDATE_ERR) {
-               nn_err(nn, "Reconfig error: 0x%08x\n", reg);
+               nn_err(nn, "Reconfig error (status: 0x%08x update: 0x%08x ctrl: 0x%08x)\n",
+                      reg, nn->reconfig_in_progress_update,
+                      nn_readl(nn, NFP_NET_CFG_CTRL));
                return true;
        } else if (last_check) {
-               nn_err(nn, "Reconfig timeout: 0x%08x\n", reg);
+               nn_err(nn, "Reconfig timeout (status: 0x%08x update: 0x%08x ctrl: 0x%08x)\n",
+                      reg, nn->reconfig_in_progress_update,
+                      nn_readl(nn, NFP_NET_CFG_CTRL));
                return true;
        }
 
@@ -279,7 +284,7 @@ int nfp_net_reconfig(struct nfp_net *nn, u32 update)
  *
  * Return: Negative errno on error, 0 on success
  */
-static int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
+int nfp_net_reconfig_mbox(struct nfp_net *nn, u32 mbox_cmd)
 {
        u32 mbox = nn->tlv_caps.mbox_off;
        int ret;
@@ -647,27 +652,29 @@ static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q,
  * @txbuf: Pointer to driver soft TX descriptor
  * @txd: Pointer to HW TX descriptor
  * @skb: Pointer to SKB
+ * @md_bytes: Prepend length
  *
  * Set up Tx descriptor for LSO, do nothing for non-LSO skbs.
  * Return error on packet header greater than maximum supported LSO header size.
  */
 static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec,
                           struct nfp_net_tx_buf *txbuf,
-                          struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+                          struct nfp_net_tx_desc *txd, struct sk_buff *skb,
+                          u32 md_bytes)
 {
-       u32 hdrlen;
+       u32 l3_offset, l4_offset, hdrlen;
        u16 mss;
 
        if (!skb_is_gso(skb))
                return;
 
        if (!skb->encapsulation) {
-               txd->l3_offset = skb_network_offset(skb);
-               txd->l4_offset = skb_transport_offset(skb);
+               l3_offset = skb_network_offset(skb);
+               l4_offset = skb_transport_offset(skb);
                hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
        } else {
-               txd->l3_offset = skb_inner_network_offset(skb);
-               txd->l4_offset = skb_inner_transport_offset(skb);
+               l3_offset = skb_inner_network_offset(skb);
+               l4_offset = skb_inner_transport_offset(skb);
                hdrlen = skb_inner_transport_header(skb) - skb->data +
                        inner_tcp_hdrlen(skb);
        }
@@ -676,7 +683,9 @@ static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec,
        txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1);
 
        mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK;
-       txd->lso_hdrlen = hdrlen;
+       txd->l3_offset = l3_offset - md_bytes;
+       txd->l4_offset = l4_offset - md_bytes;
+       txd->lso_hdrlen = hdrlen - md_bytes;
        txd->mss = cpu_to_le16(mss);
        txd->flags |= PCIE_DESC_TX_LSO;
 
@@ -786,11 +795,11 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 {
        struct nfp_net *nn = netdev_priv(netdev);
        const struct skb_frag_struct *frag;
-       struct nfp_net_tx_desc *txd, txdg;
        int f, nr_frags, wr_idx, md_bytes;
        struct nfp_net_tx_ring *tx_ring;
        struct nfp_net_r_vector *r_vec;
        struct nfp_net_tx_buf *txbuf;
+       struct nfp_net_tx_desc *txd;
        struct netdev_queue *nd_q;
        struct nfp_net_dp *dp;
        dma_addr_t dma_addr;
@@ -801,13 +810,13 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
        qidx = skb_get_queue_mapping(skb);
        tx_ring = &dp->tx_rings[qidx];
        r_vec = tx_ring->r_vec;
-       nd_q = netdev_get_tx_queue(dp->netdev, qidx);
 
        nr_frags = skb_shinfo(skb)->nr_frags;
 
        if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) {
                nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n",
                           qidx, tx_ring->wr_p, tx_ring->rd_p);
+               nd_q = netdev_get_tx_queue(dp->netdev, qidx);
                netif_tx_stop_queue(nd_q);
                nfp_net_tx_xmit_more_flush(tx_ring);
                u64_stats_update_begin(&r_vec->tx_sync);
@@ -851,7 +860,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
        txd->lso_hdrlen = 0;
 
        /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */
-       nfp_net_tx_tso(r_vec, txbuf, txd, skb);
+       nfp_net_tx_tso(r_vec, txbuf, txd, skb, md_bytes);
        nfp_net_tx_csum(dp, r_vec, txbuf, txd, skb);
        if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
                txd->flags |= PCIE_DESC_TX_VLAN;
@@ -860,8 +869,10 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 
        /* Gather DMA */
        if (nr_frags > 0) {
+               __le64 second_half;
+
                /* all descs must match except for in addr, length and eop */
-               txdg = *txd;
+               second_half = txd->vals8[1];
 
                for (f = 0; f < nr_frags; f++) {
                        frag = &skb_shinfo(skb)->frags[f];
@@ -878,11 +889,11 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
                        tx_ring->txbufs[wr_idx].fidx = f;
 
                        txd = &tx_ring->txds[wr_idx];
-                       *txd = txdg;
                        txd->dma_len = cpu_to_le16(fsize);
                        nfp_desc_set_dma_addr(txd, dma_addr);
-                       txd->offset_eop |=
-                               (f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0;
+                       txd->offset_eop = md_bytes |
+                               ((f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0);
+                       txd->vals8[1] = second_half;
                }
 
                u64_stats_update_begin(&r_vec->tx_sync);
@@ -890,16 +901,16 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
                u64_stats_update_end(&r_vec->tx_sync);
        }
 
-       netdev_tx_sent_queue(nd_q, txbuf->real_len);
-
        skb_tx_timestamp(skb);
 
+       nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx);
+
        tx_ring->wr_p += nr_frags + 1;
        if (nfp_net_tx_ring_should_stop(tx_ring))
                nfp_net_tx_ring_stop(nd_q, tx_ring);
 
        tx_ring->wr_ptr_add += nr_frags + 1;
-       if (!skb->xmit_more || netif_xmit_stopped(nd_q))
+       if (__netdev_tx_sent_queue(nd_q, txbuf->real_len, skb->xmit_more))
                nfp_net_tx_xmit_more_flush(tx_ring);
 
        return NETDEV_TX_OK;
@@ -940,14 +951,10 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
 {
        struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
        struct nfp_net_dp *dp = &r_vec->nfp_net->dp;
-       const struct skb_frag_struct *frag;
        struct netdev_queue *nd_q;
        u32 done_pkts = 0, done_bytes = 0;
-       struct sk_buff *skb;
-       int todo, nr_frags;
        u32 qcp_rd_p;
-       int fidx;
-       int idx;
+       int todo;
 
        if (tx_ring->wr_p == tx_ring->rd_p)
                return;
@@ -961,26 +968,33 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
        todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
 
        while (todo--) {
+               const struct skb_frag_struct *frag;
+               struct nfp_net_tx_buf *tx_buf;
+               struct sk_buff *skb;
+               int fidx, nr_frags;
+               int idx;
+
                idx = D_IDX(tx_ring, tx_ring->rd_p++);
+               tx_buf = &tx_ring->txbufs[idx];
 
-               skb = tx_ring->txbufs[idx].skb;
+               skb = tx_buf->skb;
                if (!skb)
                        continue;
 
                nr_frags = skb_shinfo(skb)->nr_frags;
-               fidx = tx_ring->txbufs[idx].fidx;
+               fidx = tx_buf->fidx;
 
                if (fidx == -1) {
                        /* unmap head */
-                       dma_unmap_single(dp->dev, tx_ring->txbufs[idx].dma_addr,
+                       dma_unmap_single(dp->dev, tx_buf->dma_addr,
                                         skb_headlen(skb), DMA_TO_DEVICE);
 
-                       done_pkts += tx_ring->txbufs[idx].pkt_cnt;
-                       done_bytes += tx_ring->txbufs[idx].real_len;
+                       done_pkts += tx_buf->pkt_cnt;
+                       done_bytes += tx_buf->real_len;
                } else {
                        /* unmap fragment */
                        frag = &skb_shinfo(skb)->frags[fidx];
-                       dma_unmap_page(dp->dev, tx_ring->txbufs[idx].dma_addr,
+                       dma_unmap_page(dp->dev, tx_buf->dma_addr,
                                       skb_frag_size(frag), DMA_TO_DEVICE);
                }
 
@@ -988,9 +1002,9 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
                if (fidx == nr_frags - 1)
                        napi_consume_skb(skb, budget);
 
-               tx_ring->txbufs[idx].dma_addr = 0;
-               tx_ring->txbufs[idx].skb = NULL;
-               tx_ring->txbufs[idx].fidx = -2;
+               tx_buf->dma_addr = 0;
+               tx_buf->skb = NULL;
+               tx_buf->fidx = -2;
        }
 
        tx_ring->qcp_rd_p = qcp_rd_p;
@@ -3275,7 +3289,10 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
                hdrlen = skb_inner_transport_header(skb) - skb->data +
                        inner_tcp_hdrlen(skb);
 
-               if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ))
+               /* Assume worst case scenario of having longest possible
+                * metadata prepend - 8B
+                */
+               if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ - 8))
                        features &= ~NETIF_F_GSO_MASK;
        }
 
@@ -3560,6 +3577,7 @@ void nfp_net_info(struct nfp_net *nn)
 /**
  * nfp_net_alloc() - Allocate netdev and related structure
  * @pdev:         PCI device
+ * @ctrl_bar:     PCI IOMEM with vNIC config memory
  * @needs_netdev: Whether to allocate a netdev for this vNIC
  * @max_tx_rings: Maximum number of TX rings supported by device
  * @max_rx_rings: Maximum number of RX rings supported by device
@@ -3570,11 +3588,12 @@ void nfp_net_info(struct nfp_net *nn)
  *
  * Return: NFP Net device structure, or ERR_PTR on error.
  */
-struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
-                             unsigned int max_tx_rings,
-                             unsigned int max_rx_rings)
+struct nfp_net *
+nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
+             unsigned int max_tx_rings, unsigned int max_rx_rings)
 {
        struct nfp_net *nn;
+       int err;
 
        if (needs_netdev) {
                struct net_device *netdev;
@@ -3594,6 +3613,7 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
        }
 
        nn->dp.dev = &pdev->dev;
+       nn->dp.ctrl_bar = ctrl_bar;
        nn->pdev = pdev;
 
        nn->max_tx_rings = max_tx_rings;
@@ -3616,7 +3636,19 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
 
        timer_setup(&nn->reconfig_timer, nfp_net_reconfig_timer, 0);
 
+       err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar,
+                                    &nn->tlv_caps);
+       if (err)
+               goto err_free_nn;
+
        return nn;
+
+err_free_nn:
+       if (nn->dp.netdev)
+               free_netdev(nn->dp.netdev);
+       else
+               vfree(nn);
+       return ERR_PTR(err);
 }
 
 /**
@@ -3889,11 +3921,6 @@ int nfp_net_init(struct nfp_net *nn)
                nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
        }
 
-       err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar,
-                                    &nn->tlv_caps);
-       if (err)
-               return err;
-
        if (nn->dp.netdev)
                nfp_net_netdev_init(nn);
 
index f2aaef9..6d5213b 100644 (file)
@@ -41,8 +41,8 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                data += 4;
 
                if (length % NFP_NET_CFG_TLV_LENGTH_INC) {
-                       dev_err(dev, "TLV size not multiple of %u len:%u\n",
-                               NFP_NET_CFG_TLV_LENGTH_INC, length);
+                       dev_err(dev, "TLV size not multiple of %u offset:%u len:%u\n",
+                               NFP_NET_CFG_TLV_LENGTH_INC, offset, length);
                        return -EINVAL;
                }
                if (data + length > end) {
@@ -61,14 +61,14 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                        if (!length)
                                return 0;
 
-                       dev_err(dev, "END TLV should be empty, has len:%d\n",
-                               length);
+                       dev_err(dev, "END TLV should be empty, has offset:%u len:%d\n",
+                               offset, length);
                        return -EINVAL;
                case NFP_NET_CFG_TLV_TYPE_ME_FREQ:
                        if (length != 4) {
                                dev_err(dev,
-                                       "ME FREQ TLV should be 4B, is %dB\n",
-                                       length);
+                                       "ME FREQ TLV should be 4B, is %dB offset:%u\n",
+                                       length, offset);
                                return -EINVAL;
                        }
 
@@ -90,6 +90,15 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
                                 FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr),
                                 offset, length);
                        break;
+               case NFP_NET_CFG_TLV_TYPE_REPR_CAP:
+                       if (length < 4) {
+                               dev_err(dev, "REPR CAP TLV short %dB < 4B offset:%u\n",
+                                       length, offset);
+                               return -EINVAL;
+                       }
+
+                       caps->repr_cap = readl(data);
+                       break;
                default:
                        if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
                                break;
index d7c8518..166d7f7 100644 (file)
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_ADD 1
 #define NFP_NET_CFG_MBOX_CMD_CTAG_FILTER_KILL 2
 
+#define NFP_NET_CFG_MBOX_CMD_PCI_DSCP_PRIOMAP_SET      5
+
 /**
  * VLAN filtering using general use mailbox
  * %NFP_NET_CFG_VLAN_FILTER:           Base address of VLAN filter mailbox
  * Variable, experimental IDs.  IDs designated for internal development and
  * experiments before a stable TLV ID has been allocated to a feature.  Should
  * never be present in production firmware.
+ *
+ * %NFP_NET_CFG_TLV_TYPE_REPR_CAP:
+ * Single word, equivalent of %NFP_NET_CFG_CAP for representors, features which
+ * can be used on representors.
  */
 #define NFP_NET_CFG_TLV_TYPE_UNKNOWN           0
 #define NFP_NET_CFG_TLV_TYPE_RESERVED          1
 #define NFP_NET_CFG_TLV_TYPE_MBOX              4
 #define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0     5
 #define NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1     6
+#define NFP_NET_CFG_TLV_TYPE_REPR_CAP          7
 
 struct device;
 
@@ -480,11 +487,13 @@ struct device;
  * @me_freq_mhz:       ME clock_freq (MHz)
  * @mbox_off:          vNIC mailbox area offset
  * @mbox_len:          vNIC mailbox area length
+ * @repr_cap:          capabilities for representors
  */
 struct nfp_net_tlv_caps {
        u32 me_freq_mhz;
        unsigned int mbox_off;
        unsigned int mbox_len;
+       u32 repr_cap;
 };
 
 int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
index 69b1c9b..ab7f249 100644 (file)
@@ -8,7 +8,7 @@
 
 static struct dentry *nfp_dir;
 
-static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data)
+static int nfp_rx_q_show(struct seq_file *file, void *data)
 {
        struct nfp_net_r_vector *r_vec = file->private;
        struct nfp_net_rx_ring *rx_ring;
@@ -65,31 +65,12 @@ out:
        rtnl_unlock();
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(nfp_rx_q);
 
-static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, nfp_net_debugfs_rx_q_read, inode->i_private);
-}
+static int nfp_tx_q_show(struct seq_file *file, void *data);
+DEFINE_SHOW_ATTRIBUTE(nfp_tx_q);
 
-static const struct file_operations nfp_rx_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_rx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
-
-static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f);
-
-static const struct file_operations nfp_tx_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_tx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
-
-static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
+static int nfp_tx_q_show(struct seq_file *file, void *data)
 {
        struct nfp_net_r_vector *r_vec = file->private;
        struct nfp_net_tx_ring *tx_ring;
@@ -158,18 +139,11 @@ out:
        return 0;
 }
 
-static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
+static int nfp_xdp_q_show(struct seq_file *file, void *data)
 {
-       return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private);
+       return nfp_tx_q_show(file, data);
 }
-
-static const struct file_operations nfp_xdp_q_fops = {
-       .owner = THIS_MODULE,
-       .open = nfp_net_debugfs_tx_q_open,
-       .release = single_release,
-       .read = seq_read,
-       .llseek = seq_lseek
-};
+DEFINE_SHOW_ATTRIBUTE(nfp_xdp_q);
 
 void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir)
 {
index 1e7d204..08f5fdb 100644 (file)
@@ -116,13 +116,13 @@ nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev,
        n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS);
 
        /* Allocate and initialise the vNIC */
-       nn = nfp_net_alloc(pf->pdev, needs_netdev, n_tx_rings, n_rx_rings);
+       nn = nfp_net_alloc(pf->pdev, ctrl_bar, needs_netdev,
+                          n_tx_rings, n_rx_rings);
        if (IS_ERR(nn))
                return nn;
 
        nn->app = pf->app;
        nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar);
-       nn->dp.ctrl_bar = ctrl_bar;
        nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ;
        nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ;
        nn->dp.is_vf = 0;
index c09b893..69d7aeb 100644 (file)
@@ -11,6 +11,7 @@
 #include "nfpcore/nfp_nsp.h"
 #include "nfp_app.h"
 #include "nfp_main.h"
+#include "nfp_net.h"
 #include "nfp_net_ctrl.h"
 #include "nfp_net_repr.h"
 #include "nfp_net_sriov.h"
@@ -231,6 +232,27 @@ err_port_disable:
        return err;
 }
 
+static netdev_features_t
+nfp_repr_fix_features(struct net_device *netdev, netdev_features_t features)
+{
+       struct nfp_repr *repr = netdev_priv(netdev);
+       netdev_features_t old_features = features;
+       netdev_features_t lower_features;
+       struct net_device *lower_dev;
+
+       lower_dev = repr->dst->u.port_info.lower_dev;
+
+       lower_features = lower_dev->features;
+       if (lower_features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+               lower_features |= NETIF_F_HW_CSUM;
+
+       features = netdev_intersect_features(features, lower_features);
+       features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_HW_TC);
+       features |= NETIF_F_LLTX;
+
+       return features;
+}
+
 const struct net_device_ops nfp_repr_netdev_ops = {
        .ndo_init               = nfp_app_ndo_init,
        .ndo_uninit             = nfp_app_ndo_uninit,
@@ -248,10 +270,25 @@ const struct net_device_ops nfp_repr_netdev_ops = {
        .ndo_set_vf_spoofchk    = nfp_app_set_vf_spoofchk,
        .ndo_get_vf_config      = nfp_app_get_vf_config,
        .ndo_set_vf_link_state  = nfp_app_set_vf_link_state,
+       .ndo_fix_features       = nfp_repr_fix_features,
        .ndo_set_features       = nfp_port_set_features,
        .ndo_set_mac_address    = eth_mac_addr,
 };
 
+void
+nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower)
+{
+       struct nfp_repr *repr = netdev_priv(netdev);
+
+       if (repr->dst->u.port_info.lower_dev != lower)
+               return;
+
+       netdev->gso_max_size = lower->gso_max_size;
+       netdev->gso_max_segs = lower->gso_max_segs;
+
+       netdev_update_features(netdev);
+}
+
 static void nfp_repr_clean(struct nfp_repr *repr)
 {
        unregister_netdev(repr->netdev);
@@ -281,6 +318,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
                  struct net_device *pf_netdev)
 {
        struct nfp_repr *repr = netdev_priv(netdev);
+       struct nfp_net *nn = netdev_priv(pf_netdev);
+       u32 repr_cap = nn->tlv_caps.repr_cap;
        int err;
 
        nfp_repr_set_lockdep_class(netdev);
@@ -299,6 +338,55 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
 
        SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops);
 
+       /* Set features the lower device can support with representors */
+       if (repr_cap & NFP_NET_CFG_CTRL_LIVE_ADDR)
+               netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+
+       netdev->hw_features = NETIF_F_HIGHDMA;
+       if (repr_cap & NFP_NET_CFG_CTRL_RXCSUM_ANY)
+               netdev->hw_features |= NETIF_F_RXCSUM;
+       if (repr_cap & NFP_NET_CFG_CTRL_TXCSUM)
+               netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+       if (repr_cap & NFP_NET_CFG_CTRL_GATHER)
+               netdev->hw_features |= NETIF_F_SG;
+       if ((repr_cap & NFP_NET_CFG_CTRL_LSO && nn->fw_ver.major > 2) ||
+           repr_cap & NFP_NET_CFG_CTRL_LSO2)
+               netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
+       if (repr_cap & NFP_NET_CFG_CTRL_RSS_ANY)
+               netdev->hw_features |= NETIF_F_RXHASH;
+       if (repr_cap & NFP_NET_CFG_CTRL_VXLAN) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO)
+                       netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+       }
+       if (repr_cap & NFP_NET_CFG_CTRL_NVGRE) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO)
+                       netdev->hw_features |= NETIF_F_GSO_GRE;
+       }
+       if (repr_cap & (NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE))
+               netdev->hw_enc_features = netdev->hw_features;
+
+       netdev->vlan_features = netdev->hw_features;
+
+       if (repr_cap & NFP_NET_CFG_CTRL_RXVLAN)
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+       if (repr_cap & NFP_NET_CFG_CTRL_TXVLAN) {
+               if (repr_cap & NFP_NET_CFG_CTRL_LSO2)
+                       netdev_warn(netdev, "Device advertises both TSO2 and TXVLAN. Refusing to enable TXVLAN.\n");
+               else
+                       netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+       }
+       if (repr_cap & NFP_NET_CFG_CTRL_CTAG_FILTER)
+               netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       netdev->features = netdev->hw_features;
+
+       /* Advertise but disable TSO by default. */
+       netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+       netdev->gso_max_segs = NFP_NET_LSO_MAX_SEGS;
+
+       netdev->priv_flags |= IFF_NO_QUEUE;
+       netdev->features |= NETIF_F_LLTX;
+
        if (nfp_app_has_tc(app)) {
                netdev->features |= NETIF_F_HW_TC;
                netdev->hw_features |= NETIF_F_HW_TC;
@@ -442,7 +530,9 @@ int nfp_reprs_resync_phys_ports(struct nfp_app *app)
                        continue;
 
                nfp_app_repr_preclean(app, netdev);
+               rtnl_lock();
                rcu_assign_pointer(reprs->reprs[i], NULL);
+               rtnl_unlock();
                synchronize_rcu();
                nfp_repr_clean(repr);
        }
index c412b94..e0f13df 100644 (file)
@@ -92,6 +92,8 @@ nfp_repr_get_locked(struct nfp_app *app, struct nfp_reprs *set,
                    unsigned int id);
 
 void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len);
+void
+nfp_repr_transfer_features(struct net_device *netdev, struct net_device *lower);
 int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
                  u32 cmsg_port_id, struct nfp_port *port,
                  struct net_device *pf_netdev);
index d2c1e9e..1145849 100644 (file)
@@ -172,7 +172,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
        rx_bar_off = NFP_PCIE_QUEUE(startq);
 
        /* Allocate and initialise the netdev */
-       nn = nfp_net_alloc(pdev, true, max_tx_rings, max_rx_rings);
+       nn = nfp_net_alloc(pdev, ctrl_bar, true, max_tx_rings, max_rx_rings);
        if (IS_ERR(nn)) {
                err = PTR_ERR(nn);
                goto err_ctrl_unmap;
@@ -180,7 +180,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
        vf->nn = nn;
 
        nn->fw_ver = fw_ver;
-       nn->dp.ctrl_bar = ctrl_bar;
        nn->dp.is_vf = 1;
        nn->stride_tx = stride;
        nn->stride_rx = stride;
index 25382f8..89d1739 100644 (file)
 #define LPC_FCCR_MIRRORCOUNTERCURRENT(n)       ((n) & 0xFFFF)
 
 /*
- * rxfliterctrl, rxfilterwolstatus, and rxfilterwolclear shared
+ * rxfilterctrl, rxfilterwolstatus, and rxfilterwolclear shared
  * register definitions
  */
 #define LPC_RXFLTRW_ACCEPTUNICAST              (1 << 0)
 #define LPC_RXFLTRW_ACCEPTPERFECT              (1 << 5)
 
 /*
- * rxfliterctrl register definitions
+ * rxfilterctrl register definitions
  */
 #define LPC_RXFLTRWSTS_MAGICPACKETENWOL                (1 << 12)
 #define LPC_RXFLTRWSTS_RXFILTERENWOL           (1 << 13)
@@ -783,8 +783,6 @@ static int lpc_mii_probe(struct net_device *ndev)
 
        phy_set_max_speed(phydev, SPEED_100);
 
-       phydev->advertising = phydev->supported;
-
        pldat->link = 0;
        pldat->speed = 0;
        pldat->duplex = -1;
index d9a03ab..24a9016 100644 (file)
@@ -296,6 +296,12 @@ enum qed_wol_support {
        QED_WOL_SUPPORT_PME,
 };
 
+enum qed_db_rec_exec {
+       DB_REC_DRY_RUN,
+       DB_REC_REAL_DEAL,
+       DB_REC_ONCE,
+};
+
 struct qed_hw_info {
        /* PCI personality */
        enum qed_pci_personality personality;
@@ -425,6 +431,14 @@ struct qed_qm_info {
        u8 num_pf_rls;
 };
 
+struct qed_db_recovery_info {
+       struct list_head list;
+
+       /* Lock to protect the doorbell recovery mechanism list */
+       spinlock_t lock;
+       u32 db_recovery_counter;
+};
+
 struct storm_stats {
        u32     address;
        u32     len;
@@ -522,6 +536,7 @@ struct qed_simd_fp_handler {
 
 enum qed_slowpath_wq_flag {
        QED_SLOWPATH_MFW_TLV_REQ,
+       QED_SLOWPATH_PERIODIC_DB_REC,
 };
 
 struct qed_hwfn {
@@ -640,6 +655,9 @@ struct qed_hwfn {
        /* L2-related */
        struct qed_l2_info *p_l2_info;
 
+       /* Mechanism for recovering from doorbell drop */
+       struct qed_db_recovery_info db_recovery_info;
+
        /* Nvm images number and attributes */
        struct qed_nvm_image_info nvm_info;
 
@@ -652,11 +670,12 @@ struct qed_hwfn {
        struct delayed_work iov_task;
        unsigned long iov_task_flags;
 #endif
-
-       struct z_stream_s               *stream;
+       struct z_stream_s *stream;
+       bool slowpath_wq_active;
        struct workqueue_struct *slowpath_wq;
        struct delayed_work slowpath_task;
        unsigned long slowpath_task_flags;
+       u32 periodic_db_rec_count;
 };
 
 struct pci_params {
@@ -897,6 +916,12 @@ u16 qed_get_cm_pq_idx_llt_mtc(struct qed_hwfn *p_hwfn, u8 tc);
 
 #define QED_LEADING_HWFN(dev)   (&dev->hwfns[0])
 
+/* doorbell recovery mechanism */
+void qed_db_recovery_dp(struct qed_hwfn *p_hwfn);
+void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
+                            enum qed_db_rec_exec db_exec);
+bool qed_edpm_enabled(struct qed_hwfn *p_hwfn);
+
 /* Other Linux specific common definitions */
 #define DP_NAME(cdev) ((cdev)->name)
 
@@ -931,4 +956,6 @@ int qed_mfw_fill_tlv_data(struct qed_hwfn *hwfn,
                          union qed_mfw_tlv_data *tlv_data);
 
 void qed_hw_info_set_offload_tc(struct qed_hw_info *p_info, u8 tc);
+
+void qed_periodic_db_rec_start(struct qed_hwfn *p_hwfn);
 #endif /* _QED_H */
index 88a8576..8f65514 100644 (file)
 
 static DEFINE_SPINLOCK(qm_lock);
 
+/******************** Doorbell Recovery *******************/
+/* The doorbell recovery mechanism consists of a list of entries which represent
+ * doorbelling entities (l2 queues, roce sq/rq/cqs, the slowpath spq, etc). Each
+ * entity needs to register with the mechanism and provide the parameters
+ * describing it's doorbell, including a location where last used doorbell data
+ * can be found. The doorbell execute function will traverse the list and
+ * doorbell all of the registered entries.
+ */
+struct qed_db_recovery_entry {
+       struct list_head list_entry;
+       void __iomem *db_addr;
+       void *db_data;
+       enum qed_db_rec_width db_width;
+       enum qed_db_rec_space db_space;
+       u8 hwfn_idx;
+};
+
+/* Display a single doorbell recovery entry */
+static void qed_db_recovery_dp_entry(struct qed_hwfn *p_hwfn,
+                                    struct qed_db_recovery_entry *db_entry,
+                                    char *action)
+{
+       DP_VERBOSE(p_hwfn,
+                  QED_MSG_SPQ,
+                  "(%s: db_entry %p, addr %p, data %p, width %s, %s space, hwfn %d)\n",
+                  action,
+                  db_entry,
+                  db_entry->db_addr,
+                  db_entry->db_data,
+                  db_entry->db_width == DB_REC_WIDTH_32B ? "32b" : "64b",
+                  db_entry->db_space == DB_REC_USER ? "user" : "kernel",
+                  db_entry->hwfn_idx);
+}
+
+/* Doorbell address sanity (address within doorbell bar range) */
+static bool qed_db_rec_sanity(struct qed_dev *cdev,
+                             void __iomem *db_addr, void *db_data)
+{
+       /* Make sure doorbell address is within the doorbell bar */
+       if (db_addr < cdev->doorbells ||
+           (u8 __iomem *)db_addr >
+           (u8 __iomem *)cdev->doorbells + cdev->db_size) {
+               WARN(true,
+                    "Illegal doorbell address: %p. Legal range for doorbell addresses is [%p..%p]\n",
+                    db_addr,
+                    cdev->doorbells,
+                    (u8 __iomem *)cdev->doorbells + cdev->db_size);
+               return false;
+       }
+
+       /* ake sure doorbell data pointer is not null */
+       if (!db_data) {
+               WARN(true, "Illegal doorbell data pointer: %p", db_data);
+               return false;
+       }
+
+       return true;
+}
+
+/* Find hwfn according to the doorbell address */
+static struct qed_hwfn *qed_db_rec_find_hwfn(struct qed_dev *cdev,
+                                            void __iomem *db_addr)
+{
+       struct qed_hwfn *p_hwfn;
+
+       /* In CMT doorbell bar is split down the middle between engine 0 and enigne 1 */
+       if (cdev->num_hwfns > 1)
+               p_hwfn = db_addr < cdev->hwfns[1].doorbells ?
+                   &cdev->hwfns[0] : &cdev->hwfns[1];
+       else
+               p_hwfn = QED_LEADING_HWFN(cdev);
+
+       return p_hwfn;
+}
+
+/* Add a new entry to the doorbell recovery mechanism */
+int qed_db_recovery_add(struct qed_dev *cdev,
+                       void __iomem *db_addr,
+                       void *db_data,
+                       enum qed_db_rec_width db_width,
+                       enum qed_db_rec_space db_space)
+{
+       struct qed_db_recovery_entry *db_entry;
+       struct qed_hwfn *p_hwfn;
+
+       /* Shortcircuit VFs, for now */
+       if (IS_VF(cdev)) {
+               DP_VERBOSE(cdev,
+                          QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
+               return 0;
+       }
+
+       /* Sanitize doorbell address */
+       if (!qed_db_rec_sanity(cdev, db_addr, db_data))
+               return -EINVAL;
+
+       /* Obtain hwfn from doorbell address */
+       p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);
+
+       /* Create entry */
+       db_entry = kzalloc(sizeof(*db_entry), GFP_KERNEL);
+       if (!db_entry) {
+               DP_NOTICE(cdev, "Failed to allocate a db recovery entry\n");
+               return -ENOMEM;
+       }
+
+       /* Populate entry */
+       db_entry->db_addr = db_addr;
+       db_entry->db_data = db_data;
+       db_entry->db_width = db_width;
+       db_entry->db_space = db_space;
+       db_entry->hwfn_idx = p_hwfn->my_id;
+
+       /* Display */
+       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Adding");
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_add_tail(&db_entry->list_entry, &p_hwfn->db_recovery_info.list);
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+
+       return 0;
+}
+
+/* Remove an entry from the doorbell recovery mechanism */
+int qed_db_recovery_del(struct qed_dev *cdev,
+                       void __iomem *db_addr, void *db_data)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+       struct qed_hwfn *p_hwfn;
+       int rc = -EINVAL;
+
+       /* Shortcircuit VFs, for now */
+       if (IS_VF(cdev)) {
+               DP_VERBOSE(cdev,
+                          QED_MSG_IOV, "db recovery - skipping VF doorbell\n");
+               return 0;
+       }
+
+       /* Sanitize doorbell address */
+       if (!qed_db_rec_sanity(cdev, db_addr, db_data))
+               return -EINVAL;
+
+       /* Obtain hwfn from doorbell address */
+       p_hwfn = qed_db_rec_find_hwfn(cdev, db_addr);
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               /* search according to db_data addr since db_addr is not unique (roce) */
+               if (db_entry->db_data == db_data) {
+                       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Deleting");
+                       list_del(&db_entry->list_entry);
+                       rc = 0;
+                       break;
+               }
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+
+       if (rc == -EINVAL)
+
+               DP_NOTICE(p_hwfn,
+                         "Failed to find element in list. Key (db_data addr) was %p. db_addr was %p\n",
+                         db_data, db_addr);
+       else
+               kfree(db_entry);
+
+       return rc;
+}
+
+/* Initialize the doorbell recovery mechanism */
+static int qed_db_recovery_setup(struct qed_hwfn *p_hwfn)
+{
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Setting up db recovery\n");
+
+       /* Make sure db_size was set in cdev */
+       if (!p_hwfn->cdev->db_size) {
+               DP_ERR(p_hwfn->cdev, "db_size not set\n");
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(&p_hwfn->db_recovery_info.list);
+       spin_lock_init(&p_hwfn->db_recovery_info.lock);
+       p_hwfn->db_recovery_info.db_recovery_counter = 0;
+
+       return 0;
+}
+
+/* Destroy the doorbell recovery mechanism */
+static void qed_db_recovery_teardown(struct qed_hwfn *p_hwfn)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Tearing down db recovery\n");
+       if (!list_empty(&p_hwfn->db_recovery_info.list)) {
+               DP_VERBOSE(p_hwfn,
+                          QED_MSG_SPQ,
+                          "Doorbell Recovery teardown found the doorbell recovery list was not empty (Expected in disorderly driver unload (e.g. recovery) otherwise this probably means some flow forgot to db_recovery_del). Prepare to purge doorbell recovery list...\n");
+               while (!list_empty(&p_hwfn->db_recovery_info.list)) {
+                       db_entry =
+                           list_first_entry(&p_hwfn->db_recovery_info.list,
+                                            struct qed_db_recovery_entry,
+                                            list_entry);
+                       qed_db_recovery_dp_entry(p_hwfn, db_entry, "Purging");
+                       list_del(&db_entry->list_entry);
+                       kfree(db_entry);
+               }
+       }
+       p_hwfn->db_recovery_info.db_recovery_counter = 0;
+}
+
+/* Print the content of the doorbell recovery mechanism */
+void qed_db_recovery_dp(struct qed_hwfn *p_hwfn)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       DP_NOTICE(p_hwfn,
+                 "Displaying doorbell recovery database. Counter was %d\n",
+                 p_hwfn->db_recovery_info.db_recovery_counter);
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               qed_db_recovery_dp_entry(p_hwfn, db_entry, "Printing");
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+}
+
+/* Ring the doorbell of a single doorbell recovery entry */
+static void qed_db_recovery_ring(struct qed_hwfn *p_hwfn,
+                                struct qed_db_recovery_entry *db_entry,
+                                enum qed_db_rec_exec db_exec)
+{
+       if (db_exec != DB_REC_ONCE) {
+               /* Print according to width */
+               if (db_entry->db_width == DB_REC_WIDTH_32B) {
+                       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                                  "%s doorbell address %p data %x\n",
+                                  db_exec == DB_REC_DRY_RUN ?
+                                  "would have rung" : "ringing",
+                                  db_entry->db_addr,
+                                  *(u32 *)db_entry->db_data);
+               } else {
+                       DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+                                  "%s doorbell address %p data %llx\n",
+                                  db_exec == DB_REC_DRY_RUN ?
+                                  "would have rung" : "ringing",
+                                  db_entry->db_addr,
+                                  *(u64 *)(db_entry->db_data));
+               }
+       }
+
+       /* Sanity */
+       if (!qed_db_rec_sanity(p_hwfn->cdev, db_entry->db_addr,
+                              db_entry->db_data))
+               return;
+
+       /* Flush the write combined buffer. Since there are multiple doorbelling
+        * entities using the same address, if we don't flush, a transaction
+        * could be lost.
+        */
+       wmb();
+
+       /* Ring the doorbell */
+       if (db_exec == DB_REC_REAL_DEAL || db_exec == DB_REC_ONCE) {
+               if (db_entry->db_width == DB_REC_WIDTH_32B)
+                       DIRECT_REG_WR(db_entry->db_addr,
+                                     *(u32 *)(db_entry->db_data));
+               else
+                       DIRECT_REG_WR64(db_entry->db_addr,
+                                       *(u64 *)(db_entry->db_data));
+       }
+
+       /* Flush the write combined buffer. Next doorbell may come from a
+        * different entity to the same address...
+        */
+       wmb();
+}
+
+/* Traverse the doorbell recovery entry list and ring all the doorbells */
+void qed_db_recovery_execute(struct qed_hwfn *p_hwfn,
+                            enum qed_db_rec_exec db_exec)
+{
+       struct qed_db_recovery_entry *db_entry = NULL;
+
+       if (db_exec != DB_REC_ONCE) {
+               DP_NOTICE(p_hwfn,
+                         "Executing doorbell recovery. Counter was %d\n",
+                         p_hwfn->db_recovery_info.db_recovery_counter);
+
+               /* Track amount of times recovery was executed */
+               p_hwfn->db_recovery_info.db_recovery_counter++;
+       }
+
+       /* Protect the list */
+       spin_lock_bh(&p_hwfn->db_recovery_info.lock);
+       list_for_each_entry(db_entry,
+                           &p_hwfn->db_recovery_info.list, list_entry) {
+               qed_db_recovery_ring(p_hwfn, db_entry, db_exec);
+               if (db_exec == DB_REC_ONCE)
+                       break;
+       }
+
+       spin_unlock_bh(&p_hwfn->db_recovery_info.lock);
+}
+
+/******************** Doorbell Recovery end ****************/
+
 #define QED_MIN_DPIS            (4)
 #define QED_MIN_PWM_REGION      (QED_WID_SIZE * QED_MIN_DPIS)
 
@@ -194,6 +506,9 @@ void qed_resc_free(struct qed_dev *cdev)
                qed_dmae_info_free(p_hwfn);
                qed_dcbx_info_free(p_hwfn);
                qed_dbg_user_data_free(p_hwfn);
+
+               /* Destroy doorbell recovery mechanism */
+               qed_db_recovery_teardown(p_hwfn);
        }
 }
 
@@ -969,6 +1284,11 @@ int qed_resc_alloc(struct qed_dev *cdev)
                struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
                u32 n_eqes, num_cons;
 
+               /* Initialize the doorbell recovery mechanism */
+               rc = qed_db_recovery_setup(p_hwfn);
+               if (rc)
+                       goto alloc_err;
+
                /* First allocate the context manager structure */
                rc = qed_cxt_mngr_alloc(p_hwfn);
                if (rc)
@@ -1468,6 +1788,14 @@ enum QED_ROCE_EDPM_MODE {
        QED_ROCE_EDPM_MODE_DISABLE = 2,
 };
 
+bool qed_edpm_enabled(struct qed_hwfn *p_hwfn)
+{
+       if (p_hwfn->dcbx_no_edpm || p_hwfn->db_bar_no_edpm)
+               return false;
+
+       return true;
+}
+
 static int
 qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
 {
@@ -1537,13 +1865,13 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
        p_hwfn->wid_count = (u16) n_cpus;
 
        DP_INFO(p_hwfn,
-               "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s\n",
+               "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s, page_size=%lu\n",
                norm_regsize,
                pwm_regsize,
                p_hwfn->dpi_size,
                p_hwfn->dpi_count,
-               ((p_hwfn->dcbx_no_edpm) || (p_hwfn->db_bar_no_edpm)) ?
-               "disabled" : "enabled");
+               (!qed_edpm_enabled(p_hwfn)) ?
+               "disabled" : "enabled", PAGE_SIZE);
 
        if (rc) {
                DP_ERR(p_hwfn,
index defdda1..acccd85 100644 (file)
@@ -472,6 +472,34 @@ int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *coal, void *handle);
 int
 qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle);
 
+/**
+ * @brief db_recovery_add - add doorbell information to the doorbell
+ * recovery mechanism.
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address of where db_data is stored
+ * @param db_width - doorbell is 32b pr 64b
+ * @param db_space - doorbell recovery addresses are user or kernel space
+ */
+int qed_db_recovery_add(struct qed_dev *cdev,
+                       void __iomem *db_addr,
+                       void *db_data,
+                       enum qed_db_rec_width db_width,
+                       enum qed_db_rec_space db_space);
+
+/**
+ * @brief db_recovery_del - remove doorbell information from the doorbell
+ * recovery mechanism. db_data serves as key (db_addr is not unique).
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address where db_data is stored. Serves as key for the
+ *                  entry to delete.
+ */
+int qed_db_recovery_del(struct qed_dev *cdev,
+                       void __iomem *db_addr, void *db_data);
+
 
 const char *qed_hw_get_resc_name(enum qed_resources res_id);
 #endif
index 5c221eb..7e120b5 100644 (file)
@@ -12655,6 +12655,7 @@ struct public_drv_mb {
 #define DRV_MB_PARAM_DCBX_NOTIFY_MASK          0x000000FF
 #define DRV_MB_PARAM_DCBX_NOTIFY_SHIFT         3
 
+#define DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_MBI     0x3
 #define DRV_MB_PARAM_NVM_LEN_OFFSET            24
 
 #define DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_SHIFT   0
@@ -12814,6 +12815,11 @@ struct public_drv_mb {
        union drv_union_data union_data;
 };
 
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET_MASK       0x00ffffff
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET_SHIFT      0
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE_MASK         0xff000000
+#define FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE_SHIFT                24
+
 enum MFW_DRV_MSG_TYPE {
        MFW_DRV_MSG_LINK_CHANGE,
        MFW_DRV_MSG_FLR_FW_ACK_FAILED,
index b22f464..9234091 100644 (file)
@@ -361,29 +361,147 @@ static int qed_pglub_rbc_attn_cb(struct qed_hwfn *p_hwfn)
        return 0;
 }
 
-#define QED_DORQ_ATTENTION_REASON_MASK (0xfffff)
-#define QED_DORQ_ATTENTION_OPAQUE_MASK (0xffff)
-#define QED_DORQ_ATTENTION_SIZE_MASK   (0x7f)
-#define QED_DORQ_ATTENTION_SIZE_SHIFT  (16)
+#define QED_DORQ_ATTENTION_REASON_MASK  (0xfffff)
+#define QED_DORQ_ATTENTION_OPAQUE_MASK  (0xffff)
+#define QED_DORQ_ATTENTION_OPAQUE_SHIFT (0x0)
+#define QED_DORQ_ATTENTION_SIZE_MASK            (0x7f)
+#define QED_DORQ_ATTENTION_SIZE_SHIFT           (16)
+
+#define QED_DB_REC_COUNT                        1000
+#define QED_DB_REC_INTERVAL                     100
+
+static int qed_db_rec_flush_queue(struct qed_hwfn *p_hwfn,
+                                 struct qed_ptt *p_ptt)
+{
+       u32 count = QED_DB_REC_COUNT;
+       u32 usage = 1;
+
+       /* wait for usage to zero or count to run out. This is necessary since
+        * EDPM doorbell transactions can take multiple 64b cycles, and as such
+        * can "split" over the pci. Possibly, the doorbell drop can happen with
+        * half an EDPM in the queue and other half dropped. Another EDPM
+        * doorbell to the same address (from doorbell recovery mechanism or
+        * from the doorbelling entity) could have first half dropped and second
+        * half interpreted as continuation of the first. To prevent such
+        * malformed doorbells from reaching the device, flush the queue before
+        * releasing the overflow sticky indication.
+        */
+       while (count-- && usage) {
+               usage = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_USAGE_CNT);
+               udelay(QED_DB_REC_INTERVAL);
+       }
+
+       /* should have been depleted by now */
+       if (usage) {
+               DP_NOTICE(p_hwfn->cdev,
+                         "DB recovery: doorbell usage failed to zero after %d usec. usage was %x\n",
+                         QED_DB_REC_INTERVAL * QED_DB_REC_COUNT, usage);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 overflow;
+       int rc;
+
+       overflow = qed_rd(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY);
+       DP_NOTICE(p_hwfn, "PF Overflow sticky 0x%x\n", overflow);
+       if (!overflow) {
+               qed_db_recovery_execute(p_hwfn, DB_REC_ONCE);
+               return 0;
+       }
+
+       if (qed_edpm_enabled(p_hwfn)) {
+               rc = qed_db_rec_flush_queue(p_hwfn, p_ptt);
+               if (rc)
+                       return rc;
+       }
+
+       /* Flush any pending (e)dpm as they may never arrive */
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_DPM_FORCE_ABORT, 0x1);
+
+       /* Release overflow sticky indication (stop silently dropping everything) */
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_OVFL_STICKY, 0x0);
+
+       /* Repeat all last doorbells (doorbell drop recovery) */
+       qed_db_recovery_execute(p_hwfn, DB_REC_REAL_DEAL);
+
+       return 0;
+}
+
 static int qed_dorq_attn_cb(struct qed_hwfn *p_hwfn)
 {
-       u32 reason;
+       u32 int_sts, first_drop_reason, details, address, all_drops_reason;
+       struct qed_ptt *p_ptt = p_hwfn->p_dpc_ptt;
+       int rc;
 
-       reason = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt, DORQ_REG_DB_DROP_REASON) &
-                       QED_DORQ_ATTENTION_REASON_MASK;
-       if (reason) {
-               u32 details = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                                    DORQ_REG_DB_DROP_DETAILS);
+       int_sts = qed_rd(p_hwfn, p_ptt, DORQ_REG_INT_STS);
+       DP_NOTICE(p_hwfn->cdev, "DORQ attention. int_sts was %x\n", int_sts);
 
-               DP_INFO(p_hwfn->cdev,
-                       "DORQ db_drop: address 0x%08x Opaque FID 0x%04x Size [bytes] 0x%08x Reason: 0x%08x\n",
-                       qed_rd(p_hwfn, p_hwfn->p_dpc_ptt,
-                              DORQ_REG_DB_DROP_DETAILS_ADDRESS),
-                       (u16)(details & QED_DORQ_ATTENTION_OPAQUE_MASK),
-                       GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
-                       reason);
+       /* int_sts may be zero since all PFs were interrupted for doorbell
+        * overflow but another one already handled it. Can abort here. If
+        * This PF also requires overflow recovery we will be interrupted again.
+        * The masked almost full indication may also be set. Ignoring.
+        */
+       if (!(int_sts & ~DORQ_REG_INT_STS_DORQ_FIFO_AFULL))
+               return 0;
+
+       /* check if db_drop or overflow happened */
+       if (int_sts & (DORQ_REG_INT_STS_DB_DROP |
+                      DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR)) {
+               /* Obtain data about db drop/overflow */
+               first_drop_reason = qed_rd(p_hwfn, p_ptt,
+                                          DORQ_REG_DB_DROP_REASON) &
+                   QED_DORQ_ATTENTION_REASON_MASK;
+               details = qed_rd(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS);
+               address = qed_rd(p_hwfn, p_ptt,
+                                DORQ_REG_DB_DROP_DETAILS_ADDRESS);
+               all_drops_reason = qed_rd(p_hwfn, p_ptt,
+                                         DORQ_REG_DB_DROP_DETAILS_REASON);
+
+               /* Log info */
+               DP_NOTICE(p_hwfn->cdev,
+                         "Doorbell drop occurred\n"
+                         "Address\t\t0x%08x\t(second BAR address)\n"
+                         "FID\t\t0x%04x\t\t(Opaque FID)\n"
+                         "Size\t\t0x%04x\t\t(in bytes)\n"
+                         "1st drop reason\t0x%08x\t(details on first drop since last handling)\n"
+                         "Sticky reasons\t0x%08x\t(all drop reasons since last handling)\n",
+                         address,
+                         GET_FIELD(details, QED_DORQ_ATTENTION_OPAQUE),
+                         GET_FIELD(details, QED_DORQ_ATTENTION_SIZE) * 4,
+                         first_drop_reason, all_drops_reason);
+
+               rc = qed_db_rec_handler(p_hwfn, p_ptt);
+               qed_periodic_db_rec_start(p_hwfn);
+               if (rc)
+                       return rc;
+
+               /* Clear the doorbell drop details and prepare for next drop */
+               qed_wr(p_hwfn, p_ptt, DORQ_REG_DB_DROP_DETAILS_REL, 0);
+
+               /* Mark interrupt as handled (note: even if drop was due to a different
+                * reason than overflow we mark as handled)
+                */
+               qed_wr(p_hwfn,
+                      p_ptt,
+                      DORQ_REG_INT_STS_WR,
+                      DORQ_REG_INT_STS_DB_DROP |
+                      DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR);
+
+               /* If there are no indications other than drop indications, success */
+               if ((int_sts & ~(DORQ_REG_INT_STS_DB_DROP |
+                                DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR |
+                                DORQ_REG_INT_STS_DORQ_FIFO_AFULL)) == 0)
+                       return 0;
        }
 
+       /* Some other indication was present - non recoverable */
+       DP_INFO(p_hwfn, "DORQ fatal attention\n");
+
        return -EINVAL;
 }
 
index 54b4ee0..d81a62e 100644 (file)
@@ -190,6 +190,16 @@ void qed_int_get_num_sbs(struct qed_hwfn   *p_hwfn,
  */
 void qed_int_disable_post_isr_release(struct qed_dev *cdev);
 
+/**
+ * @brief - Doorbell Recovery handler.
+ *          Run DB_REAL_DEAL doorbell recovery in case of PF overflow
+ *          (and flush DORQ if needed), otherwise run DB_REC_ONCE.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ */
+int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+
 #define QED_CAU_DEF_RX_TIMER_RES 0
 #define QED_CAU_DEF_TX_TIMER_RES 0
 
index aa63338..504c8f7 100644 (file)
@@ -1085,7 +1085,14 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
 
        p_ramrod->gsi_offload_flag = p_ll2_conn->input.gsi_enable;
 
-       return qed_spq_post(p_hwfn, p_ent, NULL);
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               return rc;
+
+       rc = qed_db_recovery_add(p_hwfn->cdev, p_tx->doorbell_addr,
+                                &p_tx->db_msg, DB_REC_WIDTH_32B,
+                                DB_REC_KERNEL);
+       return rc;
 }
 
 static int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
@@ -1119,9 +1126,11 @@ static int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
 static int qed_sp_ll2_tx_queue_stop(struct qed_hwfn *p_hwfn,
                                    struct qed_ll2_info *p_ll2_conn)
 {
+       struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
        struct qed_spq_entry *p_ent = NULL;
        struct qed_sp_init_data init_data;
        int rc = -EINVAL;
+       qed_db_recovery_del(p_hwfn->cdev, p_tx->doorbell_addr, &p_tx->db_msg);
 
        /* Get SPQ entry */
        memset(&init_data, 0, sizeof(init_data));
@@ -1542,6 +1551,13 @@ int qed_ll2_establish_connection(void *cxt, u8 connection_handle)
        p_tx->doorbell_addr = (u8 __iomem *)p_hwfn->doorbells +
                                            qed_db_addr(p_ll2_conn->cid,
                                                        DQ_DEMS_LEGACY);
+       /* prepare db data */
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
+       SET_FIELD(p_tx->db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_TX_BD_PROD_CMD);
+       p_tx->db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+
 
        rc = qed_ll2_establish_connection_rx(p_hwfn, p_ll2_conn);
        if (rc)
@@ -1780,7 +1796,6 @@ static void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
        bool b_notify = p_ll2_conn->tx_queue.cur_send_packet->notify_fw;
        struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
        struct qed_ll2_tx_packet *p_pkt = NULL;
-       struct core_db_data db_msg = { 0, 0, 0 };
        u16 bd_prod;
 
        /* If there are missing BDs, don't do anything now */
@@ -1809,24 +1824,19 @@ static void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
                list_move_tail(&p_pkt->list_entry, &p_tx->active_descq);
        }
 
-       SET_FIELD(db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
-       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
-       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
-                 DQ_XCM_CORE_TX_BD_PROD_CMD);
-       db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
-       db_msg.spq_prod = cpu_to_le16(bd_prod);
+       p_tx->db_msg.spq_prod = cpu_to_le16(bd_prod);
 
        /* Make sure the BDs data is updated before ringing the doorbell */
        wmb();
 
-       DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&db_msg));
+       DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&p_tx->db_msg));
 
        DP_VERBOSE(p_hwfn,
                   (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
                   "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Doorbelled [producer 0x%04x]\n",
                   p_ll2_conn->queue_id,
                   p_ll2_conn->cid,
-                  p_ll2_conn->input.conn_type, db_msg.spq_prod);
+                  p_ll2_conn->input.conn_type, p_tx->db_msg.spq_prod);
 }
 
 int qed_ll2_prepare_tx_packet(void *cxt,
index 1a5c1ae..5f01fbd 100644 (file)
@@ -103,6 +103,7 @@ struct qed_ll2_tx_queue {
        struct qed_ll2_tx_packet cur_completing_packet;
        u16 cur_completing_bd_idx;
        void __iomem *doorbell_addr;
+       struct core_db_data db_msg;
        u16 bds_idx;
        u16 cur_send_frag_num;
        u16 cur_completing_frag_num;
index fff7f04..6adf5bd 100644 (file)
@@ -966,9 +966,47 @@ static void qed_update_pf_params(struct qed_dev *cdev,
        }
 }
 
+#define QED_PERIODIC_DB_REC_COUNT              100
+#define QED_PERIODIC_DB_REC_INTERVAL_MS                100
+#define QED_PERIODIC_DB_REC_INTERVAL \
+       msecs_to_jiffies(QED_PERIODIC_DB_REC_INTERVAL_MS)
+#define QED_PERIODIC_DB_REC_WAIT_COUNT         10
+#define QED_PERIODIC_DB_REC_WAIT_INTERVAL \
+       (QED_PERIODIC_DB_REC_INTERVAL_MS / QED_PERIODIC_DB_REC_WAIT_COUNT)
+
+static int qed_slowpath_delayed_work(struct qed_hwfn *hwfn,
+                                    enum qed_slowpath_wq_flag wq_flag,
+                                    unsigned long delay)
+{
+       if (!hwfn->slowpath_wq_active)
+               return -EINVAL;
+
+       /* Memory barrier for setting atomic bit */
+       smp_mb__before_atomic();
+       set_bit(wq_flag, &hwfn->slowpath_task_flags);
+       smp_mb__after_atomic();
+       queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, delay);
+
+       return 0;
+}
+
+void qed_periodic_db_rec_start(struct qed_hwfn *p_hwfn)
+{
+       /* Reset periodic Doorbell Recovery counter */
+       p_hwfn->periodic_db_rec_count = QED_PERIODIC_DB_REC_COUNT;
+
+       /* Don't schedule periodic Doorbell Recovery if already scheduled */
+       if (test_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                    &p_hwfn->slowpath_task_flags))
+               return;
+
+       qed_slowpath_delayed_work(p_hwfn, QED_SLOWPATH_PERIODIC_DB_REC,
+                                 QED_PERIODIC_DB_REC_INTERVAL);
+}
+
 static void qed_slowpath_wq_stop(struct qed_dev *cdev)
 {
-       int i;
+       int i, sleep_count = QED_PERIODIC_DB_REC_WAIT_COUNT;
 
        if (IS_VF(cdev))
                return;
@@ -977,6 +1015,15 @@ static void qed_slowpath_wq_stop(struct qed_dev *cdev)
                if (!cdev->hwfns[i].slowpath_wq)
                        continue;
 
+               /* Stop queuing new delayed works */
+               cdev->hwfns[i].slowpath_wq_active = false;
+
+               /* Wait until the last periodic doorbell recovery is executed */
+               while (test_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                               &cdev->hwfns[i].slowpath_task_flags) &&
+                      sleep_count--)
+                       msleep(QED_PERIODIC_DB_REC_WAIT_INTERVAL);
+
                flush_workqueue(cdev->hwfns[i].slowpath_wq);
                destroy_workqueue(cdev->hwfns[i].slowpath_wq);
        }
@@ -989,7 +1036,10 @@ static void qed_slowpath_task(struct work_struct *work)
        struct qed_ptt *ptt = qed_ptt_acquire(hwfn);
 
        if (!ptt) {
-               queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, 0);
+               if (hwfn->slowpath_wq_active)
+                       queue_delayed_work(hwfn->slowpath_wq,
+                                          &hwfn->slowpath_task, 0);
+
                return;
        }
 
@@ -997,6 +1047,15 @@ static void qed_slowpath_task(struct work_struct *work)
                               &hwfn->slowpath_task_flags))
                qed_mfw_process_tlv_req(hwfn, ptt);
 
+       if (test_and_clear_bit(QED_SLOWPATH_PERIODIC_DB_REC,
+                              &hwfn->slowpath_task_flags)) {
+               qed_db_rec_handler(hwfn, ptt);
+               if (hwfn->periodic_db_rec_count--)
+                       qed_slowpath_delayed_work(hwfn,
+                                                 QED_SLOWPATH_PERIODIC_DB_REC,
+                                                 QED_PERIODIC_DB_REC_INTERVAL);
+       }
+
        qed_ptt_release(hwfn, ptt);
 }
 
@@ -1023,6 +1082,7 @@ static int qed_slowpath_wq_start(struct qed_dev *cdev)
                }
 
                INIT_DELAYED_WORK(&hwfn->slowpath_task, qed_slowpath_task);
+               hwfn->slowpath_wq_active = true;
        }
 
        return 0;
@@ -1939,21 +1999,30 @@ exit:
  * 0B  |                       0x3 [command index]                            |
  * 4B  | b'0: check_response?   | b'1-31  reserved                            |
  * 8B  | File-type |                   reserved                               |
+ * 12B |                    Image length in bytes                             |
  *     \----------------------------------------------------------------------/
  *     Start a new file of the provided type
  */
 static int qed_nvm_flash_image_file_start(struct qed_dev *cdev,
                                          const u8 **data, bool *check_resp)
 {
+       u32 file_type, file_size = 0;
        int rc;
 
        *data += 4;
        *check_resp = !!(**data & BIT(0));
        *data += 4;
+       file_type = **data;
 
        DP_VERBOSE(cdev, NETIF_MSG_DRV,
-                  "About to start a new file of type %02x\n", **data);
-       rc = qed_mcp_nvm_put_file_begin(cdev, **data);
+                  "About to start a new file of type %02x\n", file_type);
+       if (file_type == DRV_MB_PARAM_NVM_PUT_FILE_BEGIN_MBI) {
+               *data += 4;
+               file_size = *((u32 *)(*data));
+       }
+
+       rc = qed_mcp_nvm_write(cdev, QED_PUT_FILE_BEGIN, file_type,
+                              (u8 *)(&file_size), 4);
        *data += 4;
 
        return rc;
@@ -2315,6 +2384,8 @@ const struct qed_common_ops qed_common_ops_pass = {
        .update_mac = &qed_update_mac,
        .update_mtu = &qed_update_mtu,
        .update_wol = &qed_update_wol,
+       .db_recovery_add = &qed_db_recovery_add,
+       .db_recovery_del = &qed_db_recovery_del,
        .read_module_eeprom = &qed_read_module_eeprom,
 };
 
index a96364d..e7f18e3 100644 (file)
@@ -1619,7 +1619,7 @@ static void qed_mcp_update_stag(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                qed_sp_pf_update_stag(p_hwfn);
        }
 
-       DP_VERBOSE(p_hwfn, QED_MSG_SP, "ovlan  = %d hw_mode = 0x%x\n",
+       DP_VERBOSE(p_hwfn, QED_MSG_SP, "ovlan = %d hw_mode = 0x%x\n",
                   p_hwfn->mcp_info->func_info.ovlan, p_hwfn->hw_info.hw_mode);
 
        /* Acknowledge the MFW */
@@ -1641,7 +1641,9 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
        val = (port_cfg & OEM_CFG_CHANNEL_TYPE_MASK) >>
                OEM_CFG_CHANNEL_TYPE_OFFSET;
        if (val != OEM_CFG_CHANNEL_TYPE_STAGGED)
-               DP_NOTICE(p_hwfn, "Incorrect UFP Channel type  %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Incorrect UFP Channel type  %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
 
        val = (port_cfg & OEM_CFG_SCHED_TYPE_MASK) >> OEM_CFG_SCHED_TYPE_OFFSET;
        if (val == OEM_CFG_SCHED_TYPE_ETS) {
@@ -1650,7 +1652,9 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                p_hwfn->ufp_info.mode = QED_UFP_MODE_VNIC_BW;
        } else {
                p_hwfn->ufp_info.mode = QED_UFP_MODE_UNKNOWN;
-               DP_NOTICE(p_hwfn, "Unknown UFP scheduling mode %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Unknown UFP scheduling mode %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
        }
 
        qed_mcp_get_shmem_func(p_hwfn, p_ptt, &shmem_info, MCP_PF_ID(p_hwfn));
@@ -1665,13 +1669,15 @@ void qed_mcp_read_ufp_config(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
                p_hwfn->ufp_info.pri_type = QED_UFP_PRI_OS;
        } else {
                p_hwfn->ufp_info.pri_type = QED_UFP_PRI_UNKNOWN;
-               DP_NOTICE(p_hwfn, "Unknown Host priority control %d\n", val);
+               DP_NOTICE(p_hwfn,
+                         "Unknown Host priority control %d port_id 0x%02x\n",
+                         val, MFW_PORT(p_hwfn));
        }
 
        DP_NOTICE(p_hwfn,
-                 "UFP shmem config: mode = %d tc = %d pri_type = %d\n",
-                 p_hwfn->ufp_info.mode,
-                 p_hwfn->ufp_info.tc, p_hwfn->ufp_info.pri_type);
+                 "UFP shmem config: mode = %d tc = %d pri_type = %d port_id 0x%02x\n",
+                 p_hwfn->ufp_info.mode, p_hwfn->ufp_info.tc,
+                 p_hwfn->ufp_info.pri_type, MFW_PORT(p_hwfn));
 }
 
 static int
@@ -2739,24 +2745,6 @@ int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf)
        return 0;
 }
 
-int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr)
-{
-       struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
-       struct qed_ptt *p_ptt;
-       u32 resp, param;
-       int rc;
-
-       p_ptt = qed_ptt_acquire(p_hwfn);
-       if (!p_ptt)
-               return -EBUSY;
-       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_NVM_PUT_FILE_BEGIN, addr,
-                        &resp, &param);
-       cdev->mcp_nvm_resp = resp;
-       qed_ptt_release(p_hwfn, p_ptt);
-
-       return rc;
-}
-
 int qed_mcp_nvm_write(struct qed_dev *cdev,
                      u32 cmd, u32 addr, u8 *p_buf, u32 len)
 {
@@ -2770,6 +2758,9 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                return -EBUSY;
 
        switch (cmd) {
+       case QED_PUT_FILE_BEGIN:
+               nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_BEGIN;
+               break;
        case QED_PUT_FILE_DATA:
                nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_DATA;
                break;
@@ -2782,10 +2773,14 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                goto out;
        }
 
+       buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN);
        while (buf_idx < len) {
-               buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN);
-               nvm_offset = ((buf_size << DRV_MB_PARAM_NVM_LEN_OFFSET) |
-                             addr) + buf_idx;
+               if (cmd == QED_PUT_FILE_BEGIN)
+                       nvm_offset = addr;
+               else
+                       nvm_offset = ((buf_size <<
+                                      DRV_MB_PARAM_NVM_LEN_OFFSET) | addr) +
+                                      buf_idx;
                rc = qed_mcp_nvm_wr_cmd(p_hwfn, p_ptt, nvm_cmd, nvm_offset,
                                        &resp, &param, buf_size,
                                        (u32 *)&p_buf[buf_idx]);
@@ -2810,7 +2805,19 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                if (buf_idx % 0x1000 > (buf_idx + buf_size) % 0x1000)
                        usleep_range(1000, 2000);
 
-               buf_idx += buf_size;
+               /* For MBI upgrade, MFW response includes the next buffer offset
+                * to be delivered to MFW.
+                */
+               if (param && cmd == QED_PUT_FILE_DATA) {
+                       buf_idx = QED_MFW_GET_FIELD(param,
+                                       FW_MB_PARAM_NVM_PUT_FILE_REQ_OFFSET);
+                       buf_size = QED_MFW_GET_FIELD(param,
+                                        FW_MB_PARAM_NVM_PUT_FILE_REQ_SIZE);
+               } else {
+                       buf_idx += buf_size;
+                       buf_size = min_t(u32, (len - buf_idx),
+                                        MCP_DRV_NVM_BUF_LEN);
+               }
        }
 
        cdev->mcp_nvm_resp = resp;
index 1adfe52..eddf677 100644 (file)
@@ -543,16 +543,6 @@ int qed_mcp_nvm_write(struct qed_dev *cdev,
                      u32 cmd, u32 addr, u8 *p_buf, u32 len);
 
 /**
- * @brief Put file begin
- *
- *  @param cdev
- *  @param addr - nvm offset
- *
- * @return int - 0 - operation was successful.
- */
-int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr);
-
-/**
  * @brief Check latest response
  *
  *  @param cdev
index 2440970..8939ed6 100644 (file)
        0x1701534UL
 #define TSEM_REG_DBG_FORCE_FRAME \
        0x1701538UL
+#define DORQ_REG_PF_USAGE_CNT \
+       0x1009c0UL
+#define DORQ_REG_PF_OVFL_STICKY        \
+       0x1009d0UL
+#define DORQ_REG_DPM_FORCE_ABORT \
+       0x1009d8UL
+#define DORQ_REG_INT_STS \
+       0x100180UL
+#define DORQ_REG_INT_STS_ADDRESS_ERROR \
+       (0x1UL << 0)
+#define DORQ_REG_INT_STS_WR \
+       0x100188UL
+#define DORQ_REG_DB_DROP_DETAILS_REL \
+       0x100a28UL
+#define DORQ_REG_INT_STS_ADDRESS_ERROR_SHIFT \
+       0
+#define DORQ_REG_INT_STS_DB_DROP \
+               (0x1UL << 1)
+#define DORQ_REG_INT_STS_DB_DROP_SHIFT \
+       1
+#define DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR \
+               (0x1UL << 2)
+#define DORQ_REG_INT_STS_DORQ_FIFO_OVFL_ERR_SHIFT \
+       2
+#define DORQ_REG_INT_STS_DORQ_FIFO_AFULL\
+               (0x1UL << 3)
+#define DORQ_REG_INT_STS_DORQ_FIFO_AFULL_SHIFT \
+       3
+#define DORQ_REG_INT_STS_CFC_BYP_VALIDATION_ERR \
+               (0x1UL << 4)
+#define DORQ_REG_INT_STS_CFC_BYP_VALIDATION_ERR_SHIFT \
+       4
+#define DORQ_REG_INT_STS_CFC_LD_RESP_ERR \
+               (0x1UL << 5)
+#define DORQ_REG_INT_STS_CFC_LD_RESP_ERR_SHIFT \
+       5
+#define DORQ_REG_INT_STS_XCM_DONE_CNT_ERR \
+               (0x1UL << 6)
+#define DORQ_REG_INT_STS_XCM_DONE_CNT_ERR_SHIFT        \
+       6
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_OVFL_ERR \
+               (0x1UL << 7)
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_OVFL_ERR_SHIFT        \
+       7
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_UNDER_ERR \
+               (0x1UL << 8)
+#define DORQ_REG_INT_STS_CFC_LD_REQ_FIFO_UNDER_ERR_SHIFT \
+       8
+#define DORQ_REG_DB_DROP_DETAILS_REASON        \
+       0x100a20UL
 #define MSEM_REG_DBG_SELECT \
        0x1801528UL
 #define MSEM_REG_DBG_DWORD_ENABLE \
index 3157c0d..4179c90 100644 (file)
@@ -227,7 +227,9 @@ struct qed_spq {
        u32                     comp_count;
 
        u32                     cid;
-       qed_spq_async_comp_cb async_comp_cb[MAX_PROTOCOL_TYPE];
+       u32                     db_addr_offset;
+       struct core_db_data     db_data;
+       qed_spq_async_comp_cb   async_comp_cb[MAX_PROTOCOL_TYPE];
 };
 
 /**
index 0a9c5bb..eb88bbc 100644 (file)
@@ -252,9 +252,9 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
                           struct qed_spq *p_spq, struct qed_spq_entry *p_ent)
 {
        struct qed_chain *p_chain = &p_hwfn->p_spq->chain;
+       struct core_db_data *p_db_data = &p_spq->db_data;
        u16 echo = qed_chain_get_prod_idx(p_chain);
        struct slow_path_element        *elem;
-       struct core_db_data             db;
 
        p_ent->elem.hdr.echo    = cpu_to_le16(echo);
        elem = qed_chain_produce(p_chain);
@@ -266,27 +266,22 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
        *elem = p_ent->elem; /* struct assignment */
 
        /* send a doorbell on the slow hwfn session */
-       memset(&db, 0, sizeof(db));
-       SET_FIELD(db.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
-       SET_FIELD(db.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
-       SET_FIELD(db.params, CORE_DB_DATA_AGG_VAL_SEL,
-                 DQ_XCM_CORE_SPQ_PROD_CMD);
-       db.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
-       db.spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
+       p_db_data->spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
 
        /* make sure the SPQE is updated before the doorbell */
        wmb();
 
-       DOORBELL(p_hwfn, qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY), *(u32 *)&db);
+       DOORBELL(p_hwfn, p_spq->db_addr_offset, *(u32 *)p_db_data);
 
        /* make sure doorbell is rang */
        wmb();
 
        DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
                   "Doorbelled [0x%08x, CID 0x%08x] with Flags: %02x agg_params: %02x, prod: %04x\n",
-                  qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY),
-                  p_spq->cid, db.params, db.agg_flags,
-                  qed_chain_get_prod_idx(p_chain));
+                  p_spq->db_addr_offset,
+                  p_spq->cid,
+                  p_db_data->params,
+                  p_db_data->agg_flags, qed_chain_get_prod_idx(p_chain));
 
        return 0;
 }
@@ -490,8 +485,11 @@ void qed_spq_setup(struct qed_hwfn *p_hwfn)
 {
        struct qed_spq *p_spq = p_hwfn->p_spq;
        struct qed_spq_entry *p_virt = NULL;
+       struct core_db_data *p_db_data;
+       void __iomem *db_addr;
        dma_addr_t p_phys = 0;
        u32 i, capacity;
+       int rc;
 
        INIT_LIST_HEAD(&p_spq->pending);
        INIT_LIST_HEAD(&p_spq->completion_pending);
@@ -528,6 +526,25 @@ void qed_spq_setup(struct qed_hwfn *p_hwfn)
 
        /* reset the chain itself */
        qed_chain_reset(&p_spq->chain);
+
+       /* Initialize the address/data of the SPQ doorbell */
+       p_spq->db_addr_offset = qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY);
+       p_db_data = &p_spq->db_data;
+       memset(p_db_data, 0, sizeof(*p_db_data));
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_MAX);
+       SET_FIELD(p_db_data->params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_SPQ_PROD_CMD);
+       p_db_data->agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+
+       /* Register the SPQ doorbell with the doorbell recovery mechanism */
+       db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells +
+                                  p_spq->db_addr_offset);
+       rc = qed_db_recovery_add(p_hwfn->cdev, db_addr, &p_spq->db_data,
+                                DB_REC_WIDTH_32B, DB_REC_KERNEL);
+       if (rc)
+               DP_INFO(p_hwfn,
+                       "Failed to register the SPQ doorbell with the doorbell recovery mechanism\n");
 }
 
 int qed_spq_alloc(struct qed_hwfn *p_hwfn)
@@ -575,11 +592,17 @@ spq_allocate_fail:
 void qed_spq_free(struct qed_hwfn *p_hwfn)
 {
        struct qed_spq *p_spq = p_hwfn->p_spq;
+       void __iomem *db_addr;
        u32 capacity;
 
        if (!p_spq)
                return;
 
+       /* Delete the SPQ doorbell from the doorbell recovery mechanism */
+       db_addr = (void __iomem *)((u8 __iomem *)p_hwfn->doorbells +
+                                  p_spq->db_addr_offset);
+       qed_db_recovery_del(p_hwfn->cdev, db_addr, &p_spq->db_data);
+
        if (p_spq->p_virt) {
                capacity = qed_chain_get_capacity(&p_spq->chain);
                dma_free_coherent(&p_hwfn->cdev->pdev->dev,
index de98a97..613249d 100644 (file)
@@ -168,6 +168,13 @@ struct qede_ptp;
 
 #define QEDE_RFS_MAX_FLTR      256
 
+enum qede_flags_bit {
+       QEDE_FLAGS_IS_VF = 0,
+       QEDE_FLAGS_LINK_REQUESTED,
+       QEDE_FLAGS_PTP_TX_IN_PRORGESS,
+       QEDE_FLAGS_TX_TIMESTAMPING_EN
+};
+
 struct qede_dev {
        struct qed_dev                  *cdev;
        struct net_device               *ndev;
@@ -177,10 +184,7 @@ struct qede_dev {
        u8                              dp_level;
 
        unsigned long flags;
-#define QEDE_FLAG_IS_VF                        BIT(0)
-#define IS_VF(edev)    (!!((edev)->flags & QEDE_FLAG_IS_VF))
-#define QEDE_TX_TIMESTAMPING_EN                BIT(1)
-#define QEDE_FLAGS_PTP_TX_IN_PRORGESS  BIT(2)
+#define IS_VF(edev)    (test_bit(QEDE_FLAGS_IS_VF, &(edev)->flags))
 
        const struct qed_eth_ops        *ops;
        struct qede_ptp                 *ptp;
@@ -377,6 +381,7 @@ struct qede_tx_queue {
 
        u64 xmit_pkts;
        u64 stopped_cnt;
+       u64 tx_mem_alloc_err;
 
        __le16 *hw_cons_ptr;
 
index 8cbbd62..16331c6 100644 (file)
@@ -73,6 +73,7 @@ static const struct {
 } qede_tqstats_arr[] = {
        QEDE_TQSTAT(xmit_pkts),
        QEDE_TQSTAT(stopped_cnt),
+       QEDE_TQSTAT(tx_mem_alloc_err),
 };
 
 #define QEDE_STAT_OFFSET(stat_name, type, base) \
index 1a78027..bdf816f 100644 (file)
@@ -1466,8 +1466,8 @@ netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 #if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET)
        if (qede_pkt_req_lin(skb, xmit_type)) {
                if (skb_linearize(skb)) {
-                       DP_NOTICE(edev,
-                                 "SKB linearization failed - silently dropping this SKB\n");
+                       txq->tx_mem_alloc_err++;
+
                        dev_kfree_skb_any(skb);
                        return NETDEV_TX_OK;
                }
index 46d0f2e..5a74fcb 100644 (file)
@@ -1086,7 +1086,7 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
        }
 
        if (is_vf)
-               edev->flags |= QEDE_FLAG_IS_VF;
+               set_bit(QEDE_FLAGS_IS_VF, &edev->flags);
 
        qede_init_ndev(edev);
 
@@ -1774,6 +1774,10 @@ static int qede_drain_txq(struct qede_dev *edev,
 static int qede_stop_txq(struct qede_dev *edev,
                         struct qede_tx_queue *txq, int rss_id)
 {
+       /* delete doorbell from doorbell recovery mechanism */
+       edev->ops->common->db_recovery_del(edev->cdev, txq->doorbell_addr,
+                                          &txq->tx_db);
+
        return edev->ops->q_tx_stop(edev->cdev, rss_id, txq->handle);
 }
 
@@ -1910,6 +1914,11 @@ static int qede_start_txq(struct qede_dev *edev,
                  DQ_XCM_ETH_TX_BD_PROD_CMD);
        txq->tx_db.data.agg_flags = DQ_XCM_ETH_DQ_CF_CMD;
 
+       /* register doorbell with doorbell recovery mechanism */
+       rc = edev->ops->common->db_recovery_add(edev->cdev, txq->doorbell_addr,
+                                               &txq->tx_db, DB_REC_WIDTH_32B,
+                                               DB_REC_KERNEL);
+
        return rc;
 }
 
@@ -2057,6 +2066,8 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode,
        if (!is_locked)
                __qede_lock(edev);
 
+       clear_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags);
+
        edev->state = QEDE_STATE_CLOSED;
 
        qede_rdma_dev_event_close(edev);
@@ -2163,6 +2174,8 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode,
        /* Program un-configured VLANs */
        qede_configure_vlan_filters(edev);
 
+       set_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags);
+
        /* Ask for link-up using current configuration */
        memset(&link_params, 0, sizeof(link_params));
        link_params.link_up = true;
@@ -2258,8 +2271,8 @@ static void qede_link_update(void *dev, struct qed_link_output *link)
 {
        struct qede_dev *edev = dev;
 
-       if (!netif_running(edev->ndev)) {
-               DP_VERBOSE(edev, NETIF_MSG_LINK, "Interface is not running\n");
+       if (!test_bit(QEDE_FLAGS_LINK_REQUESTED, &edev->flags)) {
+               DP_VERBOSE(edev, NETIF_MSG_LINK, "Interface is not ready\n");
                return;
        }
 
index 013ff56..5f3f42a 100644 (file)
@@ -223,12 +223,12 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev)
 
        switch (ptp->tx_type) {
        case HWTSTAMP_TX_ON:
-               edev->flags |= QEDE_TX_TIMESTAMPING_EN;
+               set_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
                tx_type = QED_PTP_HWTSTAMP_TX_ON;
                break;
 
        case HWTSTAMP_TX_OFF:
-               edev->flags &= ~QEDE_TX_TIMESTAMPING_EN;
+               clear_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
                tx_type = QED_PTP_HWTSTAMP_TX_OFF;
                break;
 
@@ -518,7 +518,7 @@ void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb)
        if (test_and_set_bit_lock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags))
                return;
 
-       if (unlikely(!(edev->flags & QEDE_TX_TIMESTAMPING_EN))) {
+       if (unlikely(!test_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags))) {
                DP_NOTICE(edev,
                          "Tx timestamping was not enabled, this packet will not be timestamped\n");
        } else if (unlikely(ptp->tx_skb)) {
index d42ba22..16d0479 100644 (file)
@@ -2993,10 +2993,8 @@ int qlcnic_check_temp(struct qlcnic_adapter *adapter)
 static inline void dump_tx_ring_desc(struct qlcnic_host_tx_ring *tx_ring)
 {
        int i;
-       struct cmd_desc_type0 *tx_desc_info;
 
        for (i = 0; i < tx_ring->num_desc; i++) {
-               tx_desc_info = &tx_ring->desc_head[i];
                pr_info("TX Desc: %d\n", i);
                print_hex_dump(KERN_INFO, "TX: ", DUMP_PREFIX_OFFSET, 16, 1,
                               &tx_ring->desc_head[i],
@@ -4008,19 +4006,12 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
                          int queue_type)
 {
        struct net_device *netdev = adapter->netdev;
-       u8 max_hw_rings = 0;
        char buf[8];
-       int cur_rings;
 
-       if (queue_type == QLCNIC_RX_QUEUE) {
-               max_hw_rings = adapter->max_sds_rings;
-               cur_rings = adapter->drv_sds_rings;
+       if (queue_type == QLCNIC_RX_QUEUE)
                strcpy(buf, "SDS");
-       } else if (queue_type == QLCNIC_TX_QUEUE) {
-               max_hw_rings = adapter->max_tx_rings;
-               cur_rings = adapter->drv_tx_rings;
+       else
                strcpy(buf, "Tx");
-       }
 
        if (!is_power_of_2(ring_cnt)) {
                netdev_err(netdev, "%s rings value should be a power of 2\n",
index a9f1bc0..bcb890b 100644 (file)
@@ -61,6 +61,7 @@ static const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
        "Transmit ring full",
        "SPI errors",
        "Write verify errors",
+       "Buffer available errors",
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -125,19 +126,7 @@ qcaspi_info_show(struct seq_file *s, void *what)
 
        return 0;
 }
-
-static int
-qcaspi_info_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, qcaspi_info_show, inode->i_private);
-}
-
-static const struct file_operations qcaspi_info_ops = {
-       .open = qcaspi_info_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qcaspi_info);
 
 void
 qcaspi_init_device_debugfs(struct qcaspi *qca)
@@ -153,7 +142,7 @@ qcaspi_init_device_debugfs(struct qcaspi *qca)
                return;
        }
        debugfs_create_file("info", S_IFREG | 0444, device_root, qca,
-                           &qcaspi_info_ops);
+                           &qcaspi_info_fops);
 }
 
 void
index d531050..97f9295 100644 (file)
@@ -289,6 +289,14 @@ qcaspi_transmit(struct qcaspi *qca)
 
        qcaspi_read_register(qca, SPI_REG_WRBUF_SPC_AVA, &available);
 
+       if (available > QCASPI_HW_BUF_LEN) {
+               /* This could only happen by interferences on the SPI line.
+                * So retry later ...
+                */
+               qca->stats.buf_avail_err++;
+               return -1;
+       }
+
        while (qca->txr.skb[qca->txr.head]) {
                pkt_len = qca->txr.skb[qca->txr.head]->len + QCASPI_HW_PKT_LEN;
 
@@ -355,7 +363,13 @@ qcaspi_receive(struct qcaspi *qca)
        netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %08x\n",
                   available);
 
-       if (available == 0) {
+       if (available > QCASPI_HW_BUF_LEN) {
+               /* This could only happen by interferences on the SPI line.
+                * So retry later ...
+                */
+               qca->stats.buf_avail_err++;
+               return -1;
+       } else if (available == 0) {
                netdev_dbg(net_dev, "qcaspi_receive called without any data being available!\n");
                return -1;
        }
index 2d2c497..eb9af45 100644 (file)
@@ -74,6 +74,7 @@ struct qcaspi_stats {
        u64 ring_full;
        u64 spi_err;
        u64 write_verify_failed;
+       u64 buf_avail_err;
 };
 
 struct qcaspi {
index 5f4e447..b8bbee6 100644 (file)
@@ -301,10 +301,13 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
        struct rmnet_port *port;
        u16 mux_id;
 
+       if (!dev)
+               return -ENODEV;
+
        real_dev = __dev_get_by_index(dev_net(dev),
                                      nla_get_u32(tb[IFLA_LINK]));
 
-       if (!real_dev || !dev || !rmnet_is_real_dev_registered(real_dev))
+       if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
                return -ENODEV;
 
        port = rmnet_get_port_rtnl(real_dev);
index 3ee8ae9..f6cf59a 100644 (file)
@@ -20,17 +20,12 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
                                    struct rmnet_port *port,
                                    int enable)
 {
-       struct rmnet_map_control_command *cmd;
        struct rmnet_endpoint *ep;
        struct net_device *vnd;
-       u16 ip_family;
-       u16 fc_seq;
-       u32 qos_id;
        u8 mux_id;
        int r;
 
        mux_id = RMNET_MAP_GET_MUX_ID(skb);
-       cmd = RMNET_MAP_GET_CMD_START(skb);
 
        if (mux_id >= RMNET_MAX_LOGICAL_EP) {
                kfree_skb(skb);
@@ -45,10 +40,6 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
 
        vnd = ep->egress_dev;
 
-       ip_family = cmd->flow_control.ip_family;
-       fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
-       qos_id = ntohl(cmd->flow_control.qos_id);
-
        /* Ignore the ip family and pass the sequence number for both v4 and v6
         * sequence. User space does not support creating dedicated flows for
         * the 2 protocols
index 1fd0168..e26c48b 100644 (file)
 #define R8169_MSG_DEFAULT \
        (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
 
-#define TX_SLOTS_AVAIL(tp) \
-       (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx)
-
-/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
-#define TX_FRAGS_READY_FOR(tp,nr_frags) \
-       (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1))
-
 /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
    The RTL chips use a 64 element hash table based on the Ethernet CRC. */
 static const int multicast_filter_limit = 32;
@@ -212,24 +205,24 @@ enum cfg_version {
 };
 
 static const struct pci_device_id rtl8169_pci_tbl[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8129), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8136), 0, 0, RTL_CFG_2 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8161), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8167), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8168), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_NCUBE,       0x8168), 0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8169), 0, 0, RTL_CFG_0 },
-       { PCI_VENDOR_ID_DLINK,                  0x4300,
-               PCI_VENDOR_ID_DLINK, 0x4b10,             0, 0, RTL_CFG_1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4300), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4302), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_AT,          0xc107), 0, 0, RTL_CFG_0 },
-       { PCI_DEVICE(0x16ec,                    0x0116), 0, 0, RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8129), RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8136), RTL_CFG_2 },
+       { PCI_VDEVICE(REALTEK,  0x8161), RTL_CFG_1 },
+       { PCI_VDEVICE(REALTEK,  0x8167), RTL_CFG_0 },
+       { PCI_VDEVICE(REALTEK,  0x8168), RTL_CFG_1 },
+       { PCI_VDEVICE(NCUBE,    0x8168), RTL_CFG_1 },
+       { PCI_VDEVICE(REALTEK,  0x8169), RTL_CFG_0 },
+       { PCI_VENDOR_ID_DLINK,  0x4300,
+               PCI_VENDOR_ID_DLINK, 0x4b10, 0, 0, RTL_CFG_1 },
+       { PCI_VDEVICE(DLINK,    0x4300), RTL_CFG_0 },
+       { PCI_VDEVICE(DLINK,    0x4302), RTL_CFG_0 },
+       { PCI_VDEVICE(AT,       0xc107), RTL_CFG_0 },
+       { PCI_VDEVICE(USR,      0x0116), RTL_CFG_0 },
        { PCI_VENDOR_ID_LINKSYS,                0x1032,
                PCI_ANY_ID, 0x0024, 0, 0, RTL_CFG_0 },
        { 0x0001,                               0x8168,
                PCI_ANY_ID, 0x2410, 0, 0, RTL_CFG_2 },
-       {0,},
+       {}
 };
 
 MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
@@ -603,7 +596,6 @@ struct RxDesc {
 struct ring_info {
        struct sk_buff  *skb;
        u32             len;
-       u8              __pad[sizeof(void *) - sizeof(u32)];
 };
 
 struct rtl8169_counters {
@@ -661,7 +653,7 @@ struct rtl8169_private {
        struct ring_info tx_skb[NUM_TX_DESC];   /* Tx data buffers */
        u16 cp_cmd;
 
-       u16 event_slow;
+       u16 irq_mask;
        const struct rtl_coalesce_info *coalesce_info;
        struct clk *clk;
 
@@ -1102,23 +1094,6 @@ static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
        return rtl_eri_read(tp, reg, ERIAR_OOB);
 }
 
-static u32 ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
-{
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
-               return r8168dp_ocp_read(tp, mask, reg);
-       case RTL_GIGA_MAC_VER_49:
-       case RTL_GIGA_MAC_VER_50:
-       case RTL_GIGA_MAC_VER_51:
-               return r8168ep_ocp_read(tp, mask, reg);
-       default:
-               BUG();
-               return ~0;
-       }
-}
-
 static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
                              u32 data)
 {
@@ -1134,30 +1109,11 @@ static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
                      data, ERIAR_OOB);
 }
 
-static void ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, u32 data)
-{
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
-               r8168dp_ocp_write(tp, mask, reg, data);
-               break;
-       case RTL_GIGA_MAC_VER_49:
-       case RTL_GIGA_MAC_VER_50:
-       case RTL_GIGA_MAC_VER_51:
-               r8168ep_ocp_write(tp, mask, reg, data);
-               break;
-       default:
-               BUG();
-               break;
-       }
-}
-
-static void rtl8168_oob_notify(struct rtl8169_private *tp, u8 cmd)
+static void r8168dp_oob_notify(struct rtl8169_private *tp, u8 cmd)
 {
        rtl_eri_write(tp, 0xe8, ERIAR_MASK_0001, cmd, ERIAR_EXGMAC);
 
-       ocp_write(tp, 0x1, 0x30, 0x00000001);
+       r8168dp_ocp_write(tp, 0x1, 0x30, 0x00000001);
 }
 
 #define OOB_CMD_RESET          0x00
@@ -1169,18 +1125,18 @@ static u16 rtl8168_get_ocp_reg(struct rtl8169_private *tp)
        return (tp->mac_version == RTL_GIGA_MAC_VER_31) ? 0xb8 : 0x10;
 }
 
-DECLARE_RTL_COND(rtl_ocp_read_cond)
+DECLARE_RTL_COND(rtl_dp_ocp_read_cond)
 {
        u16 reg;
 
        reg = rtl8168_get_ocp_reg(tp);
 
-       return ocp_read(tp, 0x0f, reg) & 0x00000800;
+       return r8168dp_ocp_read(tp, 0x0f, reg) & 0x00000800;
 }
 
 DECLARE_RTL_COND(rtl_ep_ocp_read_cond)
 {
-       return ocp_read(tp, 0x0f, 0x124) & 0x00000001;
+       return r8168ep_ocp_read(tp, 0x0f, 0x124) & 0x00000001;
 }
 
 DECLARE_RTL_COND(rtl_ocp_tx_cond)
@@ -1198,14 +1154,15 @@ static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
 
 static void rtl8168dp_driver_start(struct rtl8169_private *tp)
 {
-       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_START);
-       rtl_msleep_loop_wait_high(tp, &rtl_ocp_read_cond, 10, 10);
+       r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START);
+       rtl_msleep_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10, 10);
 }
 
 static void rtl8168ep_driver_start(struct rtl8169_private *tp)
 {
-       ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
-       ocp_write(tp, 0x01, 0x30, ocp_read(tp, 0x01, 0x30) | 0x01);
+       r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
+       r8168ep_ocp_write(tp, 0x01, 0x30,
+                         r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
        rtl_msleep_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10, 10);
 }
 
@@ -1230,15 +1187,16 @@ static void rtl8168_driver_start(struct rtl8169_private *tp)
 
 static void rtl8168dp_driver_stop(struct rtl8169_private *tp)
 {
-       rtl8168_oob_notify(tp, OOB_CMD_DRIVER_STOP);
-       rtl_msleep_loop_wait_low(tp, &rtl_ocp_read_cond, 10, 10);
+       r8168dp_oob_notify(tp, OOB_CMD_DRIVER_STOP);
+       rtl_msleep_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10, 10);
 }
 
 static void rtl8168ep_driver_stop(struct rtl8169_private *tp)
 {
        rtl8168ep_stop_cmac(tp);
-       ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP);
-       ocp_write(tp, 0x01, 0x30, ocp_read(tp, 0x01, 0x30) | 0x01);
+       r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP);
+       r8168ep_ocp_write(tp, 0x01, 0x30,
+                         r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
        rtl_msleep_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10, 10);
 }
 
@@ -1265,12 +1223,12 @@ static bool r8168dp_check_dash(struct rtl8169_private *tp)
 {
        u16 reg = rtl8168_get_ocp_reg(tp);
 
-       return !!(ocp_read(tp, 0x0f, reg) & 0x00008000);
+       return !!(r8168dp_ocp_read(tp, 0x0f, reg) & 0x00008000);
 }
 
 static bool r8168ep_check_dash(struct rtl8169_private *tp)
 {
-       return !!(ocp_read(tp, 0x0f, 0x128) & 0x00000001);
+       return !!(r8168ep_ocp_read(tp, 0x0f, 0x128) & 0x00000001);
 }
 
 static bool r8168_check_dash(struct rtl8169_private *tp)
@@ -1325,27 +1283,20 @@ static u16 rtl_get_events(struct rtl8169_private *tp)
 static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
 {
        RTL_W16(tp, IntrStatus, bits);
-       mmiowb();
 }
 
 static void rtl_irq_disable(struct rtl8169_private *tp)
 {
        RTL_W16(tp, IntrMask, 0);
-       mmiowb();
-}
-
-static void rtl_irq_enable(struct rtl8169_private *tp, u16 bits)
-{
-       RTL_W16(tp, IntrMask, bits);
 }
 
 #define RTL_EVENT_NAPI_RX      (RxOK | RxErr)
 #define RTL_EVENT_NAPI_TX      (TxOK | TxErr)
 #define RTL_EVENT_NAPI         (RTL_EVENT_NAPI_RX | RTL_EVENT_NAPI_TX)
 
-static void rtl_irq_enable_all(struct rtl8169_private *tp)
+static void rtl_irq_enable(struct rtl8169_private *tp)
 {
-       rtl_irq_enable(tp, RTL_EVENT_NAPI | tp->event_slow);
+       RTL_W16(tp, IntrMask, tp->irq_mask);
 }
 
 static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
@@ -2051,8 +2002,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
 };
 
-static void rtl8169_get_mac_version(struct rtl8169_private *tp,
-                                   u8 default_version)
+static void rtl8169_get_mac_version(struct rtl8169_private *tp)
 {
        /*
         * The driver currently handles the 8168Bf and the 8168Be identically
@@ -2066,120 +2016,107 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
         * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
         */
        static const struct rtl_mac_info {
-               u32 mask;
-               u32 val;
-               int mac_version;
+               u16 mask;
+               u16 val;
+               u16 mac_version;
        } mac_info[] = {
                /* 8168EP family. */
-               { 0x7cf00000, 0x50200000,       RTL_GIGA_MAC_VER_51 },
-               { 0x7cf00000, 0x50100000,       RTL_GIGA_MAC_VER_50 },
-               { 0x7cf00000, 0x50000000,       RTL_GIGA_MAC_VER_49 },
+               { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 },
+               { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 },
+               { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 },
 
                /* 8168H family. */
-               { 0x7cf00000, 0x54100000,       RTL_GIGA_MAC_VER_46 },
-               { 0x7cf00000, 0x54000000,       RTL_GIGA_MAC_VER_45 },
+               { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 },
+               { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 },
 
                /* 8168G family. */
-               { 0x7cf00000, 0x5c800000,       RTL_GIGA_MAC_VER_44 },
-               { 0x7cf00000, 0x50900000,       RTL_GIGA_MAC_VER_42 },
-               { 0x7cf00000, 0x4c100000,       RTL_GIGA_MAC_VER_41 },
-               { 0x7cf00000, 0x4c000000,       RTL_GIGA_MAC_VER_40 },
+               { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
+               { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 },
+               { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 },
+               { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 },
 
                /* 8168F family. */
-               { 0x7c800000, 0x48800000,       RTL_GIGA_MAC_VER_38 },
-               { 0x7cf00000, 0x48100000,       RTL_GIGA_MAC_VER_36 },
-               { 0x7cf00000, 0x48000000,       RTL_GIGA_MAC_VER_35 },
+               { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 },
+               { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
+               { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 },
 
                /* 8168E family. */
-               { 0x7c800000, 0x2c800000,       RTL_GIGA_MAC_VER_34 },
-               { 0x7cf00000, 0x2c100000,       RTL_GIGA_MAC_VER_32 },
-               { 0x7c800000, 0x2c000000,       RTL_GIGA_MAC_VER_33 },
+               { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 },
+               { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 },
+               { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 },
 
                /* 8168D family. */
-               { 0x7cf00000, 0x28100000,       RTL_GIGA_MAC_VER_25 },
-               { 0x7c800000, 0x28000000,       RTL_GIGA_MAC_VER_26 },
+               { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 },
+               { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 },
 
                /* 8168DP family. */
-               { 0x7cf00000, 0x28800000,       RTL_GIGA_MAC_VER_27 },
-               { 0x7cf00000, 0x28a00000,       RTL_GIGA_MAC_VER_28 },
-               { 0x7cf00000, 0x28b00000,       RTL_GIGA_MAC_VER_31 },
+               { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 },
+               { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 },
+               { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 },
 
                /* 8168C family. */
-               { 0x7cf00000, 0x3c900000,       RTL_GIGA_MAC_VER_23 },
-               { 0x7cf00000, 0x3c800000,       RTL_GIGA_MAC_VER_18 },
-               { 0x7c800000, 0x3c800000,       RTL_GIGA_MAC_VER_24 },
-               { 0x7cf00000, 0x3c000000,       RTL_GIGA_MAC_VER_19 },
-               { 0x7cf00000, 0x3c200000,       RTL_GIGA_MAC_VER_20 },
-               { 0x7cf00000, 0x3c300000,       RTL_GIGA_MAC_VER_21 },
-               { 0x7c800000, 0x3c000000,       RTL_GIGA_MAC_VER_22 },
+               { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 },
+               { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 },
+               { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 },
+               { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 },
+               { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 },
+               { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 },
+               { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 },
 
                /* 8168B family. */
-               { 0x7cf00000, 0x38000000,       RTL_GIGA_MAC_VER_12 },
-               { 0x7c800000, 0x38000000,       RTL_GIGA_MAC_VER_17 },
-               { 0x7c800000, 0x30000000,       RTL_GIGA_MAC_VER_11 },
+               { 0x7cf, 0x380, RTL_GIGA_MAC_VER_12 },
+               { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
+               { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
 
                /* 8101 family. */
-               { 0x7c800000, 0x44800000,       RTL_GIGA_MAC_VER_39 },
-               { 0x7c800000, 0x44000000,       RTL_GIGA_MAC_VER_37 },
-               { 0x7cf00000, 0x40900000,       RTL_GIGA_MAC_VER_29 },
-               { 0x7c800000, 0x40800000,       RTL_GIGA_MAC_VER_30 },
-               { 0x7cf00000, 0x34900000,       RTL_GIGA_MAC_VER_08 },
-               { 0x7cf00000, 0x24900000,       RTL_GIGA_MAC_VER_08 },
-               { 0x7cf00000, 0x34800000,       RTL_GIGA_MAC_VER_07 },
-               { 0x7cf00000, 0x24800000,       RTL_GIGA_MAC_VER_07 },
-               { 0x7cf00000, 0x34000000,       RTL_GIGA_MAC_VER_13 },
-               { 0x7cf00000, 0x34300000,       RTL_GIGA_MAC_VER_10 },
-               { 0x7cf00000, 0x34200000,       RTL_GIGA_MAC_VER_16 },
-               { 0x7c800000, 0x34800000,       RTL_GIGA_MAC_VER_09 },
-               { 0x7c800000, 0x24800000,       RTL_GIGA_MAC_VER_09 },
-               { 0x7c800000, 0x34000000,       RTL_GIGA_MAC_VER_16 },
+               { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
+               { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 },
+               { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 },
+               { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 },
+               { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 },
+               { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 },
+               { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 },
+               { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 },
+               { 0x7cf, 0x340, RTL_GIGA_MAC_VER_13 },
+               { 0x7cf, 0x343, RTL_GIGA_MAC_VER_10 },
+               { 0x7cf, 0x342, RTL_GIGA_MAC_VER_16 },
+               { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 },
+               { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 },
+               { 0x7c8, 0x340, RTL_GIGA_MAC_VER_16 },
                /* FIXME: where did these entries come from ? -- FR */
-               { 0xfc800000, 0x38800000,       RTL_GIGA_MAC_VER_15 },
-               { 0xfc800000, 0x30800000,       RTL_GIGA_MAC_VER_14 },
+               { 0xfc8, 0x388, RTL_GIGA_MAC_VER_15 },
+               { 0xfc8, 0x308, RTL_GIGA_MAC_VER_14 },
 
                /* 8110 family. */
-               { 0xfc800000, 0x98000000,       RTL_GIGA_MAC_VER_06 },
-               { 0xfc800000, 0x18000000,       RTL_GIGA_MAC_VER_05 },
-               { 0xfc800000, 0x10000000,       RTL_GIGA_MAC_VER_04 },
-               { 0xfc800000, 0x04000000,       RTL_GIGA_MAC_VER_03 },
-               { 0xfc800000, 0x00800000,       RTL_GIGA_MAC_VER_02 },
-               { 0xfc800000, 0x00000000,       RTL_GIGA_MAC_VER_01 },
+               { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 },
+               { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 },
+               { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 },
+               { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 },
+               { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 },
+               { 0xfc8, 0x000, RTL_GIGA_MAC_VER_01 },
 
                /* Catch-all */
-               { 0x00000000, 0x00000000,       RTL_GIGA_MAC_NONE   }
+               { 0x000, 0x000, RTL_GIGA_MAC_NONE   }
        };
        const struct rtl_mac_info *p = mac_info;
-       u32 reg;
+       u16 reg = RTL_R32(tp, TxConfig) >> 20;
 
-       reg = RTL_R32(tp, TxConfig);
        while ((reg & p->mask) != p->val)
                p++;
        tp->mac_version = p->mac_version;
 
        if (tp->mac_version == RTL_GIGA_MAC_NONE) {
-               dev_notice(tp_to_dev(tp),
-                          "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->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_42 :
-                                 RTL_GIGA_MAC_VER_43;
-       } else if (tp->mac_version == RTL_GIGA_MAC_VER_45) {
-               tp->mac_version = tp->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_45 :
-                                 RTL_GIGA_MAC_VER_47;
-       } else if (tp->mac_version == RTL_GIGA_MAC_VER_46) {
-               tp->mac_version = tp->supports_gmii ?
-                                 RTL_GIGA_MAC_VER_46 :
-                                 RTL_GIGA_MAC_VER_48;
+               dev_err(tp_to_dev(tp), "unknown chip XID %03x\n", reg & 0xfcf);
+       } else if (!tp->supports_gmii) {
+               if (tp->mac_version == RTL_GIGA_MAC_VER_42)
+                       tp->mac_version = RTL_GIGA_MAC_VER_43;
+               else if (tp->mac_version == RTL_GIGA_MAC_VER_45)
+                       tp->mac_version = RTL_GIGA_MAC_VER_47;
+               else if (tp->mac_version == RTL_GIGA_MAC_VER_46)
+                       tp->mac_version = RTL_GIGA_MAC_VER_48;
        }
 }
 
-static void rtl8169_print_mac_version(struct rtl8169_private *tp)
-{
-       netif_dbg(tp, drv, tp->dev, "mac_version = 0x%02x\n", tp->mac_version);
-}
-
 struct phy_reg {
        u16 reg;
        u16 val;
@@ -3902,8 +3839,6 @@ static void rtl_hw_phy_config(struct net_device *dev)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
 
-       rtl8169_print_mac_version(tp);
-
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_01:
                break;
@@ -4643,7 +4578,7 @@ static void rtl_hw_start(struct  rtl8169_private *tp)
        rtl_set_rx_mode(tp->dev);
        /* no early-rx interrupts */
        RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
-       rtl_irq_enable_all(tp);
+       rtl_irq_enable(tp);
 }
 
 static void rtl_hw_start_8169(struct rtl8169_private *tp)
@@ -5394,8 +5329,8 @@ static void rtl_hw_start_8168(struct rtl8169_private *tp)
 
        /* Work around for RxFIFO overflow. */
        if (tp->mac_version == RTL_GIGA_MAC_VER_11) {
-               tp->event_slow |= RxFIFOOver | PCSTimeout;
-               tp->event_slow &= ~RxOverflow;
+               tp->irq_mask |= RxFIFOOver;
+               tp->irq_mask &= ~RxOverflow;
        }
 
        switch (tp->mac_version) {
@@ -5632,7 +5567,7 @@ static void rtl_hw_start_8106(struct rtl8169_private *tp)
 static void rtl_hw_start_8101(struct rtl8169_private *tp)
 {
        if (tp->mac_version >= RTL_GIGA_MAC_VER_30)
-               tp->event_slow &= ~RxFIFOOver;
+               tp->irq_mask &= ~RxFIFOOver;
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
            tp->mac_version == RTL_GIGA_MAC_VER_16)
@@ -5888,6 +5823,16 @@ static void rtl8169_tx_timeout(struct net_device *dev)
        rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
 }
 
+static __le32 rtl8169_get_txd_opts1(u32 opts0, u32 len, unsigned int entry)
+{
+       u32 status = opts0 | len;
+
+       if (entry == NUM_TX_DESC - 1)
+               status |= RingEnd;
+
+       return cpu_to_le32(status);
+}
+
 static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
                              u32 *opts)
 {
@@ -5900,7 +5845,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
        for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
                const skb_frag_t *frag = info->frags + cur_frag;
                dma_addr_t mapping;
-               u32 status, len;
+               u32 len;
                void *addr;
 
                entry = (entry + 1) % NUM_TX_DESC;
@@ -5916,11 +5861,7 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
                        goto err_out;
                }
 
-               /* Anti gcc 2.95.3 bugware (sic) */
-               status = opts[0] | len |
-                       (RingEnd * !((entry + 1) % NUM_TX_DESC));
-
-               txd->opts1 = cpu_to_le32(status);
+               txd->opts1 = rtl8169_get_txd_opts1(opts[0], len, entry);
                txd->opts2 = cpu_to_le32(opts[1]);
                txd->addr = cpu_to_le64(mapping);
 
@@ -6108,6 +6049,15 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp,
        return true;
 }
 
+static bool rtl_tx_slots_avail(struct rtl8169_private *tp,
+                              unsigned int nr_frags)
+{
+       unsigned int slots_avail = tp->dirty_tx + NUM_TX_DESC - tp->cur_tx;
+
+       /* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
+       return slots_avail > nr_frags;
+}
+
 static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
@@ -6116,11 +6066,11 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
        struct TxDesc *txd = tp->TxDescArray + entry;
        struct device *d = tp_to_dev(tp);
        dma_addr_t mapping;
-       u32 status, len;
-       u32 opts[2];
+       u32 opts[2], len;
+       bool stop_queue;
        int frags;
 
-       if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) {
+       if (unlikely(!rtl_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
                netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
                goto err_stop_0;
        }
@@ -6159,32 +6109,26 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
 
        txd->opts2 = cpu_to_le32(opts[1]);
 
-       netdev_sent_queue(dev, skb->len);
-
        skb_tx_timestamp(skb);
 
        /* Force memory writes to complete before releasing descriptor */
        dma_wmb();
 
-       /* Anti gcc 2.95.3 bugware (sic) */
-       status = opts[0] | len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
-       txd->opts1 = cpu_to_le32(status);
+       txd->opts1 = rtl8169_get_txd_opts1(opts[0], len, entry);
 
        /* Force all memory writes to complete before notifying device */
        wmb();
 
        tp->cur_tx += frags + 1;
 
-       RTL_W8(tp, TxPoll, NPQ);
+       stop_queue = !rtl_tx_slots_avail(tp, MAX_SKB_FRAGS);
+       if (unlikely(stop_queue))
+               netif_stop_queue(dev);
 
-       mmiowb();
+       if (__netdev_sent_queue(dev, skb->len, skb->xmit_more))
+               RTL_W8(tp, TxPoll, NPQ);
 
-       if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
-               /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
-                * not miss a ring update when it notices a stopped queue.
-                */
-               smp_wmb();
-               netif_stop_queue(dev);
+       if (unlikely(stop_queue)) {
                /* Sync with rtl_tx:
                 * - publish queue status and cur_tx ring index (write barrier)
                 * - refresh dirty_tx ring index (read barrier).
@@ -6193,7 +6137,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
                 * can't.
                 */
                smp_mb();
-               if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS))
+               if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS))
                        netif_wake_queue(dev);
        }
 
@@ -6257,7 +6201,8 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev)
        rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
 }
 
-static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
+static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
+                  int budget)
 {
        unsigned int dirty_tx, tx_left, bytes_compl = 0, pkts_compl = 0;
 
@@ -6285,7 +6230,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
                if (status & LastFrag) {
                        pkts_compl++;
                        bytes_compl += tx_skb->skb->len;
-                       dev_consume_skb_any(tx_skb->skb);
+                       napi_consume_skb(tx_skb->skb, budget);
                        tx_skb->skb = NULL;
                }
                dirty_tx++;
@@ -6310,7 +6255,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
                 */
                smp_mb();
                if (netif_queue_stopped(dev) &&
-                   TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
+                   rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
                        netif_wake_queue(dev);
                }
                /*
@@ -6461,7 +6406,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
        struct rtl8169_private *tp = dev_instance;
        u16 status = rtl_get_events(tp);
 
-       if (status == 0xffff || !(status & (RTL_EVENT_NAPI | tp->event_slow)))
+       if (status == 0xffff || !(status & tp->irq_mask))
                return IRQ_NONE;
 
        if (unlikely(status & SYSErr)) {
@@ -6528,13 +6473,11 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
 
        work_done = rtl_rx(dev, tp, (u32) budget);
 
-       rtl_tx(dev, tp);
+       rtl_tx(dev, tp, budget);
 
        if (work_done < budget) {
                napi_complete_done(napi, work_done);
-
-               rtl_irq_enable_all(tp);
-               mmiowb();
+               rtl_irq_enable(tp);
        }
 
        return work_done;
@@ -6584,7 +6527,7 @@ static int r8169_phy_connect(struct rtl8169_private *tp)
                phy_set_max_speed(phydev, SPEED_100);
 
        /* Ensure to advertise everything, incl. pause */
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        phy_attached_info(phydev);
 
@@ -6824,8 +6767,7 @@ static void rtl8169_net_suspend(struct net_device *dev)
 
 static int rtl8169_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        rtl8169_net_suspend(dev);
@@ -6855,8 +6797,7 @@ static void __rtl8169_resume(struct net_device *dev)
 
 static int rtl8169_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        clk_prepare_enable(tp->clk);
@@ -6869,8 +6810,7 @@ static int rtl8169_resume(struct device *device)
 
 static int rtl8169_runtime_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
 
        if (!tp->TxDescArray)
@@ -6891,8 +6831,7 @@ static int rtl8169_runtime_suspend(struct device *device)
 
 static int rtl8169_runtime_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rtl8169_private *tp = netdev_priv(dev);
        rtl_rar_set(tp, dev->dev_addr);
 
@@ -6910,8 +6849,7 @@ static int rtl8169_runtime_resume(struct device *device)
 
 static int rtl8169_runtime_idle(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
 
        if (!netif_running(dev) || !netif_carrier_ok(dev))
                pm_schedule_suspend(device, 10000);
@@ -7023,31 +6961,26 @@ static const struct net_device_ops rtl_netdev_ops = {
 
 static const struct rtl_cfg_info {
        void (*hw_start)(struct rtl8169_private *tp);
-       u16 event_slow;
+       u16 irq_mask;
        unsigned int has_gmii:1;
        const struct rtl_coalesce_info *coalesce_info;
-       u8 default_ver;
 } rtl_cfg_infos [] = {
        [RTL_CFG_0] = {
                .hw_start       = rtl_hw_start_8169,
-               .event_slow     = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
+               .irq_mask       = SYSErr | LinkChg | RxOverflow | RxFIFOOver,
                .has_gmii       = 1,
                .coalesce_info  = rtl_coalesce_info_8169,
-               .default_ver    = RTL_GIGA_MAC_VER_01,
        },
        [RTL_CFG_1] = {
                .hw_start       = rtl_hw_start_8168,
-               .event_slow     = SYSErr | LinkChg | RxOverflow,
+               .irq_mask       = LinkChg | RxOverflow,
                .has_gmii       = 1,
                .coalesce_info  = rtl_coalesce_info_8168_8136,
-               .default_ver    = RTL_GIGA_MAC_VER_11,
        },
        [RTL_CFG_2] = {
                .hw_start       = rtl_hw_start_8101,
-               .event_slow     = SYSErr | LinkChg | RxOverflow | RxFIFOOver |
-                                 PCSTimeout,
+               .irq_mask       = LinkChg | RxOverflow | RxFIFOOver,
                .coalesce_info  = rtl_coalesce_info_8168_8136,
-               .default_ver    = RTL_GIGA_MAC_VER_13,
        }
 };
 
@@ -7309,11 +7242,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        tp->mmio_addr = pcim_iomap_table(pdev)[region];
 
-       if (!pci_is_pcie(pdev))
-               dev_info(&pdev->dev, "not PCI Express\n");
-
        /* Identify chip attached to board */
-       rtl8169_get_mac_version(tp, cfg->default_ver);
+       rtl8169_get_mac_version(tp);
+       if (tp->mac_version == RTL_GIGA_MAC_NONE)
+               return -ENODEV;
 
        if (rtl_tbi_enabled(tp)) {
                dev_err(&pdev->dev, "TBI fiber mode not supported\n");
@@ -7351,8 +7283,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        rtl_init_mdio_ops(tp);
        rtl_init_jumbo_ops(tp);
 
-       rtl8169_print_mac_version(tp);
-
        chipset = tp->mac_version;
 
        rc = rtl_alloc_irq(tp);
@@ -7426,7 +7356,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->max_mtu = jumbo_max;
 
        tp->hw_start = cfg->hw_start;
-       tp->event_slow = cfg->event_slow;
+       tp->irq_mask = RTL_EVENT_NAPI | cfg->irq_mask;
        tp->coalesce_info = cfg->coalesce_info;
 
        tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
@@ -7450,9 +7380,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto err_mdio_unregister;
 
-       netif_info(tp, probe, dev, "%s, %pM, XID %08x, IRQ %d\n",
+       netif_info(tp, probe, dev, "%s, %pM, XID %03x, IRQ %d\n",
                   rtl_chip_infos[chipset].name, dev->dev_addr,
-                  (u32)(RTL_R32(tp, TxConfig) & 0xfcf0f8ff),
+                  (RTL_R32(tp, TxConfig) >> 20) & 0xfcf,
                   pci_irq_vector(pdev, 0));
 
        if (jumbo_max > JUMBO_1K)
index 1c6e4df..ac9195a 100644 (file)
@@ -1032,7 +1032,6 @@ struct ravb_private {
        phy_interface_t phy_interface;
        int msg_enable;
        int speed;
-       int duplex;
        int emac_irq;
        enum ravb_chip_id chip_id;
        int rx_irqs[NUM_RX_QUEUE];
index defed0d..ffc1ada 100644 (file)
@@ -82,13 +82,6 @@ static int ravb_config(struct net_device *ndev)
        return error;
 }
 
-static void ravb_set_duplex(struct net_device *ndev)
-{
-       struct ravb_private *priv = netdev_priv(ndev);
-
-       ravb_modify(ndev, ECMR, ECMR_DM, priv->duplex ? ECMR_DM : 0);
-}
-
 static void ravb_set_rate(struct net_device *ndev)
 {
        struct ravb_private *priv = netdev_priv(ndev);
@@ -406,13 +399,11 @@ error:
 /* E-MAC init function */
 static void ravb_emac_init(struct net_device *ndev)
 {
-       struct ravb_private *priv = netdev_priv(ndev);
-
        /* Receive frame limit set register */
        ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
 
        /* EMAC Mode: PAUSE prohibition; Duplex; RX Checksum; TX; RX */
-       ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
+       ravb_write(ndev, ECMR_ZPF | ECMR_DM |
                   (ndev->features & NETIF_F_RXCSUM ? ECMR_RCSC : 0) |
                   ECMR_TE | ECMR_RE, ECMR);
 
@@ -995,12 +986,6 @@ static void ravb_adjust_link(struct net_device *ndev)
                ravb_rcv_snd_disable(ndev);
 
        if (phydev->link) {
-               if (phydev->duplex != priv->duplex) {
-                       new_state = true;
-                       priv->duplex = phydev->duplex;
-                       ravb_set_duplex(ndev);
-               }
-
                if (phydev->speed != priv->speed) {
                        new_state = true;
                        priv->speed = phydev->speed;
@@ -1015,7 +1000,6 @@ static void ravb_adjust_link(struct net_device *ndev)
                new_state = true;
                priv->link = 0;
                priv->speed = 0;
-               priv->duplex = -1;
        }
 
        /* Enable TX and RX right over here, if E-MAC change is ignored */
@@ -1045,7 +1029,6 @@ static int ravb_phy_init(struct net_device *ndev)
 
        priv->link = 0;
        priv->speed = 0;
-       priv->duplex = -1;
 
        /* Try connecting to PHY */
        pn = of_parse_phandle(np, "phy-handle", 0);
@@ -1088,6 +1071,10 @@ static int ravb_phy_init(struct net_device *ndev)
        phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_Pause_BIT);
        phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT);
 
+       /* Half Duplex is not supported */
+       phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
+       phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
+
        phy_attached_info(phydev);
 
        return 0;
index beb0662..6213827 100644 (file)
@@ -1632,9 +1632,6 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (!wops->port_obj_vlan_add)
                return -EOPNOTSUPP;
 
@@ -2145,8 +2142,6 @@ static int rocker_port_obj_del(struct net_device *dev,
 static const struct switchdev_ops rocker_port_switchdev_ops = {
        .switchdev_port_attr_get        = rocker_port_attr_get,
        .switchdev_port_attr_set        = rocker_port_attr_set,
-       .switchdev_port_obj_add         = rocker_port_obj_add,
-       .switchdev_port_obj_del         = rocker_port_obj_del,
 };
 
 struct rocker_fib_event_work {
@@ -2812,12 +2807,54 @@ static int rocker_switchdev_event(struct notifier_block *unused,
        return NOTIFY_DONE;
 }
 
+static int
+rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = rocker_port_obj_add(netdev, port_obj_info->obj,
+                                         port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = rocker_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int rocker_switchdev_blocking_event(struct notifier_block *unused,
+                                          unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!rocker_port_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return rocker_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block rocker_switchdev_notifier = {
        .notifier_call = rocker_switchdev_event,
 };
 
+static struct notifier_block rocker_switchdev_blocking_notifier = {
+       .notifier_call = rocker_switchdev_blocking_event,
+};
+
 static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+       struct notifier_block *nb;
        struct rocker *rocker;
        int err;
 
@@ -2933,6 +2970,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_register_switchdev_notifier;
        }
 
+       nb = &rocker_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register switchdev blocking notifier\n");
+               goto err_register_switchdev_blocking_notifier;
+       }
+
        rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
 
        dev_info(&pdev->dev, "Rocker switch with id %*phN\n",
@@ -2940,6 +2984,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        return 0;
 
+err_register_switchdev_blocking_notifier:
+       unregister_switchdev_notifier(&rocker_switchdev_notifier);
 err_register_switchdev_notifier:
        unregister_fib_notifier(&rocker->fib_nb);
 err_register_fib_notifier:
@@ -2971,6 +3017,10 @@ err_pci_enable_device:
 static void rocker_remove(struct pci_dev *pdev)
 {
        struct rocker *rocker = pci_get_drvdata(pdev);
+       struct notifier_block *nb;
+
+       nb = &rocker_switchdev_blocking_notifier;
+       unregister_switchdev_blocking_notifier(nb);
 
        unregister_switchdev_notifier(&rocker_switchdev_notifier);
        unregister_fib_notifier(&rocker->fib_nb);
index 7eeac3d..b6a5005 100644 (file)
@@ -6041,6 +6041,10 @@ static const struct efx_ef10_nvram_type_info efx_ef10_nvram_types[] = {
        { NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 0,   3, "sfc_exp_rom_cfg" },
        { NVRAM_PARTITION_TYPE_LICENSE,            0,    0, "sfc_license" },
        { NVRAM_PARTITION_TYPE_PHY_MIN,            0xff, 0, "sfc_phy_fw" },
+       /* MUM and SUC firmware share the same partition type */
+       { NVRAM_PARTITION_TYPE_MUM_FIRMWARE,       0,    0, "sfc_mumfw" },
+       { NVRAM_PARTITION_TYPE_EXPANSION_UEFI,     0,    0, "sfc_uefi" },
+       { NVRAM_PARTITION_TYPE_STATUS,             0,    0, "sfc_status" }
 };
 
 static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
@@ -6091,6 +6095,9 @@ static int efx_ef10_mtd_probe_partition(struct efx_nic *efx,
        part->common.mtd.flags = MTD_CAP_NORFLASH;
        part->common.mtd.size = size;
        part->common.mtd.erasesize = erase_size;
+       /* sfc_status is read-only */
+       if (!erase_size)
+               part->common.mtd.flags |= MTD_NO_ERASE;
 
        return 0;
 }
index 3143588..600d7b8 100644 (file)
@@ -539,7 +539,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
        /* We need rx buffers and interrupts. */
        already_up = (efx->net_dev->flags & IFF_UP);
        if (!already_up) {
-               rc = dev_open(efx->net_dev);
+               rc = dev_open(efx->net_dev, NULL);
                if (rc) {
                        netif_err(efx, drv, efx->net_dev,
                                  "failed opening device.\n");
index 1ccdb7a..72cedec 100644 (file)
@@ -517,7 +517,7 @@ static void ef4_ethtool_self_test(struct net_device *net_dev,
        /* We need rx buffers and interrupts. */
        already_up = (efx->net_dev->flags & IFF_UP);
        if (!already_up) {
-               rc = dev_open(efx->net_dev);
+               rc = dev_open(efx->net_dev, NULL);
                if (rc) {
                        netif_err(efx, drv, efx->net_dev,
                                  "failed opening device.\n");
index c3ad564..22eb059 100644 (file)
@@ -553,13 +553,10 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        if (!data_mapped && (efx_tx_map_data(tx_queue, skb, segments)))
                goto err;
 
-       /* Update BQL */
-       netdev_tx_sent_queue(tx_queue->core_txq, skb_len);
-
        efx_tx_maybe_stop_queue(tx_queue);
 
        /* Pass off to hardware */
-       if (!xmit_more || netif_xmit_stopped(tx_queue->core_txq)) {
+       if (__netdev_tx_sent_queue(tx_queue->core_txq, skb_len, xmit_more)) {
                struct efx_tx_queue *txq2 = efx_tx_queue_partner(tx_queue);
 
                /* There could be packets left on the partner queue if those
index 3588202..7961206 100644 (file)
@@ -27,7 +27,7 @@ config SMC9194
          option if you have a DELL laptop with the docking station, or
          another SMC9192/9194 based chipset.  Say Y if you want it compiled
          into the kernel, and read the file
-         <file:Documentation/networking/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
 
          To compile this driver as a module, choose M here. The module
          will be called smc9194.
@@ -43,7 +43,7 @@ config SMC91X
          This is a driver for SMC's 91x series of Ethernet chipsets,
          including the SMC91C94 and the SMC91C111. Say Y if you want it
          compiled into the kernel, and read the file
-         <file:Documentation/networking/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
 
          This driver is also available as a module ( = code which can be
          inserted in and removed from the running kernel whenever you want).
index d9d0d03..bba9733 100644 (file)
 
 #define DESC_NUM       256
 
+#define NETSEC_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN)
+#define NETSEC_RX_BUF_SZ 1536
+
 #define DESC_SZ        sizeof(struct netsec_de)
 
 #define NETSEC_F_NETSEC_VER_MAJOR_NUM(x)       ((x) & 0xffff0000)
@@ -571,34 +574,10 @@ static const struct ethtool_ops netsec_ethtool_ops = {
 
 /************* NETDEV_OPS FOLLOW *************/
 
-static struct sk_buff *netsec_alloc_skb(struct netsec_priv *priv,
-                                       struct netsec_desc *desc)
-{
-       struct sk_buff *skb;
-
-       if (device_get_dma_attr(priv->dev) == DEV_DMA_COHERENT) {
-               skb = netdev_alloc_skb_ip_align(priv->ndev, desc->len);
-       } else {
-               desc->len = L1_CACHE_ALIGN(desc->len);
-               skb = netdev_alloc_skb(priv->ndev, desc->len);
-       }
-       if (!skb)
-               return NULL;
-
-       desc->addr = skb->data;
-       desc->dma_addr = dma_map_single(priv->dev, desc->addr, desc->len,
-                                       DMA_FROM_DEVICE);
-       if (dma_mapping_error(priv->dev, desc->dma_addr)) {
-               dev_kfree_skb_any(skb);
-               return NULL;
-       }
-       return skb;
-}
 
 static void netsec_set_rx_de(struct netsec_priv *priv,
                             struct netsec_desc_ring *dring, u16 idx,
-                            const struct netsec_desc *desc,
-                            struct sk_buff *skb)
+                            const struct netsec_desc *desc)
 {
        struct netsec_de *de = dring->vaddr + DESC_SZ * idx;
        u32 attr = (1 << NETSEC_RX_PKT_OWN_FIELD) |
@@ -617,59 +596,6 @@ static void netsec_set_rx_de(struct netsec_priv *priv,
        dring->desc[idx].dma_addr = desc->dma_addr;
        dring->desc[idx].addr = desc->addr;
        dring->desc[idx].len = desc->len;
-       dring->desc[idx].skb = skb;
-}
-
-static struct sk_buff *netsec_get_rx_de(struct netsec_priv *priv,
-                                       struct netsec_desc_ring *dring,
-                                       u16 idx,
-                                       struct netsec_rx_pkt_info *rxpi,
-                                       struct netsec_desc *desc, u16 *len)
-{
-       struct netsec_de de = {};
-
-       memcpy(&de, dring->vaddr + DESC_SZ * idx, DESC_SZ);
-
-       *len = de.buf_len_info >> 16;
-
-       rxpi->err_flag = (de.attr >> NETSEC_RX_PKT_ER_FIELD) & 1;
-       rxpi->rx_cksum_result = (de.attr >> NETSEC_RX_PKT_CO_FIELD) & 3;
-       rxpi->err_code = (de.attr >> NETSEC_RX_PKT_ERR_FIELD) &
-                                                       NETSEC_RX_PKT_ERR_MASK;
-       *desc = dring->desc[idx];
-       return desc->skb;
-}
-
-static struct sk_buff *netsec_get_rx_pkt_data(struct netsec_priv *priv,
-                                             struct netsec_rx_pkt_info *rxpi,
-                                             struct netsec_desc *desc,
-                                             u16 *len)
-{
-       struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
-       struct sk_buff *tmp_skb, *skb = NULL;
-       struct netsec_desc td;
-       int tail;
-
-       *rxpi = (struct netsec_rx_pkt_info){};
-
-       td.len = priv->ndev->mtu + 22;
-
-       tmp_skb = netsec_alloc_skb(priv, &td);
-
-       tail = dring->tail;
-
-       if (!tmp_skb) {
-               netsec_set_rx_de(priv, dring, tail, &dring->desc[tail],
-                                dring->desc[tail].skb);
-       } else {
-               skb = netsec_get_rx_de(priv, dring, tail, rxpi, desc, len);
-               netsec_set_rx_de(priv, dring, tail, &td, tmp_skb);
-       }
-
-       /* move tail ahead */
-       dring->tail = (dring->tail + 1) % DESC_NUM;
-
-       return skb;
 }
 
 static int netsec_clean_tx_dring(struct netsec_priv *priv, int budget)
@@ -736,19 +662,65 @@ static int netsec_process_tx(struct netsec_priv *priv, int budget)
        return done;
 }
 
+static void *netsec_alloc_rx_data(struct netsec_priv *priv,
+                                 dma_addr_t *dma_handle, u16 *desc_len)
+{
+       size_t total_len = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       size_t payload_len = NETSEC_RX_BUF_SZ;
+       dma_addr_t mapping;
+       void *buf;
+
+       total_len += SKB_DATA_ALIGN(payload_len + NETSEC_SKB_PAD);
+
+       buf = napi_alloc_frag(total_len);
+       if (!buf)
+               return NULL;
+
+       mapping = dma_map_single(priv->dev, buf + NETSEC_SKB_PAD, payload_len,
+                                DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(priv->dev, mapping)))
+               goto err_out;
+
+       *dma_handle = mapping;
+       *desc_len = payload_len;
+
+       return buf;
+
+err_out:
+       skb_free_frag(buf);
+       return NULL;
+}
+
+static void netsec_rx_fill(struct netsec_priv *priv, u16 from, u16 num)
+{
+       struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
+       u16 idx = from;
+
+       while (num) {
+               netsec_set_rx_de(priv, dring, idx, &dring->desc[idx]);
+               idx++;
+               if (idx >= DESC_NUM)
+                       idx = 0;
+               num--;
+       }
+}
+
 static int netsec_process_rx(struct netsec_priv *priv, int budget)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
        struct net_device *ndev = priv->ndev;
        struct netsec_rx_pkt_info rx_info;
-       int done = 0;
-       struct netsec_desc desc;
        struct sk_buff *skb;
-       u16 len;
+       int done = 0;
 
        while (done < budget) {
                u16 idx = dring->tail;
                struct netsec_de *de = dring->vaddr + (DESC_SZ * idx);
+               struct netsec_desc *desc = &dring->desc[idx];
+               u16 pkt_len, desc_len;
+               dma_addr_t dma_handle;
+               void *buf_addr;
+               u32 truesize;
 
                if (de->attr & (1U << NETSEC_RX_PKT_OWN_FIELD)) {
                        /* reading the register clears the irq */
@@ -762,18 +734,59 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget)
                 */
                dma_rmb();
                done++;
-               skb = netsec_get_rx_pkt_data(priv, &rx_info, &desc, &len);
-               if (unlikely(!skb) || rx_info.err_flag) {
+
+               pkt_len = de->buf_len_info >> 16;
+               rx_info.err_code = (de->attr >> NETSEC_RX_PKT_ERR_FIELD) &
+                       NETSEC_RX_PKT_ERR_MASK;
+               rx_info.err_flag = (de->attr >> NETSEC_RX_PKT_ER_FIELD) & 1;
+               if (rx_info.err_flag) {
                        netif_err(priv, drv, priv->ndev,
-                                 "%s: rx fail err(%d)\n",
-                                 __func__, rx_info.err_code);
+                                 "%s: rx fail err(%d)\n", __func__,
+                                 rx_info.err_code);
                        ndev->stats.rx_dropped++;
+                       dring->tail = (dring->tail + 1) % DESC_NUM;
+                       /* reuse buffer page frag */
+                       netsec_rx_fill(priv, idx, 1);
                        continue;
                }
+               rx_info.rx_cksum_result =
+                       (de->attr >> NETSEC_RX_PKT_CO_FIELD) & 3;
 
-               dma_unmap_single(priv->dev, desc.dma_addr, desc.len,
-                                DMA_FROM_DEVICE);
-               skb_put(skb, len);
+               /* allocate a fresh buffer and map it to the hardware.
+                * This will eventually replace the old buffer in the hardware
+                */
+               buf_addr = netsec_alloc_rx_data(priv, &dma_handle, &desc_len);
+               if (unlikely(!buf_addr))
+                       break;
+
+               dma_sync_single_for_cpu(priv->dev, desc->dma_addr, pkt_len,
+                                       DMA_FROM_DEVICE);
+               prefetch(desc->addr);
+
+               truesize = SKB_DATA_ALIGN(desc->len + NETSEC_SKB_PAD) +
+                       SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+               skb = build_skb(desc->addr, truesize);
+               if (unlikely(!skb)) {
+                       /* free the newly allocated buffer, we are not going to
+                        * use it
+                        */
+                       dma_unmap_single(priv->dev, dma_handle, desc_len,
+                                        DMA_FROM_DEVICE);
+                       skb_free_frag(buf_addr);
+                       netif_err(priv, drv, priv->ndev,
+                                 "rx failed to build skb\n");
+                       break;
+               }
+               dma_unmap_single_attrs(priv->dev, desc->dma_addr, desc->len,
+                                      DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+
+               /* Update the descriptor with the new buffer we allocated */
+               desc->len = desc_len;
+               desc->dma_addr = dma_handle;
+               desc->addr = buf_addr;
+
+               skb_reserve(skb, NETSEC_SKB_PAD);
+               skb_put(skb, pkt_len);
                skb->protocol = eth_type_trans(skb, priv->ndev);
 
                if (priv->rx_cksum_offload_flag &&
@@ -782,8 +795,11 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget)
 
                if (napi_gro_receive(&priv->napi, skb) != GRO_DROP) {
                        ndev->stats.rx_packets++;
-                       ndev->stats.rx_bytes += len;
+                       ndev->stats.rx_bytes += pkt_len;
                }
+
+               netsec_rx_fill(priv, idx, 1);
+               dring->tail = (dring->tail + 1) % DESC_NUM;
        }
 
        return done;
@@ -946,7 +962,10 @@ static void netsec_uninit_pkt_dring(struct netsec_priv *priv, int id)
                dma_unmap_single(priv->dev, desc->dma_addr, desc->len,
                                 id == NETSEC_RING_RX ? DMA_FROM_DEVICE :
                                                              DMA_TO_DEVICE);
-               dev_kfree_skb(desc->skb);
+               if (id == NETSEC_RING_RX)
+                       skb_free_frag(desc->addr);
+               else if (id == NETSEC_RING_TX)
+                       dev_kfree_skb(desc->skb);
        }
 
        memset(dring->desc, 0, sizeof(struct netsec_desc) * DESC_NUM);
@@ -977,47 +996,50 @@ static void netsec_free_dring(struct netsec_priv *priv, int id)
 static int netsec_alloc_dring(struct netsec_priv *priv, enum ring_id id)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[id];
-       int ret = 0;
 
        dring->vaddr = dma_zalloc_coherent(priv->dev, DESC_SZ * DESC_NUM,
                                           &dring->desc_dma, GFP_KERNEL);
-       if (!dring->vaddr) {
-               ret = -ENOMEM;
+       if (!dring->vaddr)
                goto err;
-       }
 
        dring->desc = kcalloc(DESC_NUM, sizeof(*dring->desc), GFP_KERNEL);
-       if (!dring->desc) {
-               ret = -ENOMEM;
+       if (!dring->desc)
                goto err;
-       }
 
        return 0;
 err:
        netsec_free_dring(priv, id);
 
-       return ret;
+       return -ENOMEM;
 }
 
 static int netsec_setup_rx_dring(struct netsec_priv *priv)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
-       struct netsec_desc desc;
-       struct sk_buff *skb;
-       int n;
+       int i;
 
-       desc.len = priv->ndev->mtu + 22;
+       for (i = 0; i < DESC_NUM; i++) {
+               struct netsec_desc *desc = &dring->desc[i];
+               dma_addr_t dma_handle;
+               void *buf;
+               u16 len;
 
-       for (n = 0; n < DESC_NUM; n++) {
-               skb = netsec_alloc_skb(priv, &desc);
-               if (!skb) {
+               buf = netsec_alloc_rx_data(priv, &dma_handle, &len);
+               if (!buf) {
                        netsec_uninit_pkt_dring(priv, NETSEC_RING_RX);
-                       return -ENOMEM;
+                       goto err_out;
                }
-               netsec_set_rx_de(priv, dring, n, &desc, skb);
+               desc->dma_addr = dma_handle;
+               desc->addr = buf;
+               desc->len = len;
        }
 
+       netsec_rx_fill(priv, 0, DESC_NUM);
+
        return 0;
+
+err_out:
+       return -ENOMEM;
 }
 
 static int netsec_netdev_load_ucode_region(struct netsec_priv *priv, u32 reg,
@@ -1377,6 +1399,8 @@ static int netsec_netdev_init(struct net_device *ndev)
        int ret;
        u16 data;
 
+       BUILD_BUG_ON_NOT_POWER_OF_2(DESC_NUM);
+
        ret = netsec_alloc_dring(priv, NETSEC_RING_TX);
        if (ret)
                return ret;
index 7c7cd9d..bb6d5fb 100644 (file)
@@ -262,6 +262,7 @@ struct ave_private {
        struct regmap           *regmap;
        unsigned int            pinmode_mask;
        unsigned int            pinmode_val;
+       u32                     wolopts;
 
        /* stats */
        struct ave_stats        stats_rx;
@@ -1119,7 +1120,7 @@ static void ave_phy_adjust_link(struct net_device *ndev)
                if (phydev->asym_pause)
                        rmt_adv |= LPA_PAUSE_ASYM;
 
-               lcl_adv = ethtool_adv_to_lcl_adv_t(phydev->advertising);
+               lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
                cap = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
                if (cap & FLOW_CTRL_TX)
                        txcr |= AVE_TXCR_FLOCTR;
@@ -1210,9 +1211,13 @@ static int ave_init(struct net_device *ndev)
 
        priv->phydev = phydev;
 
-       phy_ethtool_get_wol(phydev, &wol);
+       ave_ethtool_get_wol(ndev, &wol);
        device_set_wakeup_capable(&ndev->dev, !!wol.supported);
 
+       /* set wol initial state disabled */
+       wol.wolopts = 0;
+       ave_ethtool_set_wol(ndev, &wol);
+
        if (!phy_interface_is_rgmii(phydev))
                phy_set_max_speed(phydev, SPEED_100);
 
@@ -1737,6 +1742,58 @@ static int ave_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int ave_suspend(struct device *dev)
+{
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct ave_private *priv = netdev_priv(ndev);
+       int ret = 0;
+
+       if (netif_running(ndev)) {
+               ret = ave_stop(ndev);
+               netif_device_detach(ndev);
+       }
+
+       ave_ethtool_get_wol(ndev, &wol);
+       priv->wolopts = wol.wolopts;
+
+       return ret;
+}
+
+static int ave_resume(struct device *dev)
+{
+       struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct ave_private *priv = netdev_priv(ndev);
+       int ret = 0;
+
+       ave_global_reset(ndev);
+
+       ave_ethtool_get_wol(ndev, &wol);
+       wol.wolopts = priv->wolopts;
+       ave_ethtool_set_wol(ndev, &wol);
+
+       if (ndev->phydev) {
+               ret = phy_resume(ndev->phydev);
+               if (ret)
+                       return ret;
+       }
+
+       if (netif_running(ndev)) {
+               ret = ave_open(ndev);
+               netif_device_attach(ndev);
+       }
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ave_pm_ops, ave_suspend, ave_resume);
+#define AVE_PM_OPS     (&ave_pm_ops)
+#else
+#define AVE_PM_OPS     NULL
+#endif
+
 static int ave_pro4_get_pinmode(struct ave_private *priv,
                                phy_interface_t phy_mode, u32 arg)
 {
@@ -1911,6 +1968,7 @@ static struct platform_driver ave_driver = {
        .remove = ave_remove,
        .driver = {
                .name = "ave",
+               .pm   = AVE_PM_OPS,
                .of_match_table = of_ave_match,
        },
 };
index 5710864..d1f61c2 100644 (file)
@@ -458,8 +458,10 @@ stmmac_get_pauseparam(struct net_device *netdev,
                if (!adv_lp.pause)
                        return;
        } else {
-               if (!(netdev->phydev->supported & SUPPORTED_Pause) ||
-                   !(netdev->phydev->supported & SUPPORTED_Asym_Pause))
+               if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                      netdev->phydev->supported) ||
+                   linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                     netdev->phydev->supported))
                        return;
        }
 
@@ -487,8 +489,10 @@ stmmac_set_pauseparam(struct net_device *netdev,
                if (!adv_lp.pause)
                        return -EOPNOTSUPP;
        } else {
-               if (!(phy->supported & SUPPORTED_Pause) ||
-                   !(phy->supported & SUPPORTED_Asym_Pause))
+               if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                      phy->supported) ||
+                   linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                     phy->supported))
                        return -EOPNOTSUPP;
        }
 
index 5551fea..e821ccc 100644 (file)
@@ -3881,7 +3881,7 @@ static void sysfs_display_ring(void *head, int size, int extend_desc,
        }
 }
 
-static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
+static int stmmac_rings_status_show(struct seq_file *seq, void *v)
 {
        struct net_device *dev = seq->private;
        struct stmmac_priv *priv = netdev_priv(dev);
@@ -3926,23 +3926,9 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(stmmac_rings_status);
 
-static int stmmac_sysfs_ring_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stmmac_sysfs_ring_read, inode->i_private);
-}
-
-/* Debugfs files, should appear in /sys/kernel/debug/stmmaceth/eth0 */
-
-static const struct file_operations stmmac_rings_status_fops = {
-       .owner = THIS_MODULE,
-       .open = stmmac_sysfs_ring_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
+static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
 {
        struct net_device *dev = seq->private;
        struct stmmac_priv *priv = netdev_priv(dev);
@@ -4005,19 +3991,7 @@ static int stmmac_sysfs_dma_cap_read(struct seq_file *seq, void *v)
 
        return 0;
 }
-
-static int stmmac_sysfs_dma_cap_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, stmmac_sysfs_dma_cap_read, inode->i_private);
-}
-
-static const struct file_operations stmmac_dma_cap_fops = {
-       .owner = THIS_MODULE,
-       .open = stmmac_sysfs_dma_cap_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(stmmac_dma_cap);
 
 static int stmmac_init_fs(struct net_device *dev)
 {
@@ -4101,7 +4075,7 @@ static void stmmac_reset_subtask(struct stmmac_priv *priv)
 
        set_bit(STMMAC_DOWN, &priv->state);
        dev_close(priv->dev);
-       dev_open(priv->dev);
+       dev_open(priv->dev, NULL);
        clear_bit(STMMAC_DOWN, &priv->state);
        clear_bit(STMMAC_RESETING, &priv->state);
        rtnl_unlock();
index 863fd60..ff641cf 100644 (file)
@@ -2691,7 +2691,7 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
        sbus_dp = op->dev.parent->of_node;
 
        /* We can match PCI devices too, do not accept those here. */
-       if (strcmp(sbus_dp->name, "sbus") && strcmp(sbus_dp->name, "sbi"))
+       if (!of_node_name_eq(sbus_dp, "sbus") && !of_node_name_eq(sbus_dp, "sbi"))
                return err;
 
        if (is_qfe) {
index f932923..bb126be 100644 (file)
@@ -121,7 +121,8 @@ config TLAN
 
          Devices currently supported by this driver are Compaq Netelligent,
          Compaq NetFlex and Olicom cards.  Please read the file
-         <file:Documentation/networking/tlan.txt> for more details.
+         <file:Documentation/networking/device_drivers/ti/tlan.txt>
+         for more details.
 
          To compile this driver as a module, choose M here. The module
          will be called tlan.
index 500f7ed..0e8f61a 100644 (file)
@@ -283,7 +283,7 @@ struct cpsw_ss_regs {
 
 #define CTRL_V2_TS_BITS \
        (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
-        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN)
+        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V2_ALL_TS_MASK (CTRL_V2_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V2_TX_TS_BITS  (CTRL_V2_TS_BITS | TS_TX_EN)
@@ -293,7 +293,7 @@ struct cpsw_ss_regs {
 #define CTRL_V3_TS_BITS \
        (TS_107 | TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
         TS_TTL_NONZERO | TS_ANNEX_F_EN | TS_ANNEX_D_EN |\
-        TS_LTYPE1_EN)
+        TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V3_ALL_TS_MASK (CTRL_V3_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V3_TX_TS_BITS  (CTRL_V3_TS_BITS | TS_TX_EN)
@@ -466,6 +466,8 @@ struct cpsw_priv {
        bool                            mqprio_hw;
        int                             fifo_bw[CPSW_TC_NUM];
        int                             shp_cfg_speed;
+       int                             tx_ts_enabled;
+       int                             rx_ts_enabled;
        u32 emac_port;
        struct cpsw_common *cpsw;
 };
@@ -565,26 +567,14 @@ static const struct cpsw_stats cpsw_gstrings_ch_stats[] = {
                                (func)(slave++, ##arg);                 \
        } while (0)
 
+static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev,
+                                   __be16 proto, u16 vid);
+
 static inline int cpsw_get_slave_port(u32 slave_num)
 {
        return slave_num + 1;
 }
 
-static void cpsw_add_mcast(struct cpsw_priv *priv, const u8 *addr)
-{
-       struct cpsw_common *cpsw = priv->cpsw;
-
-       if (cpsw->data.dual_emac) {
-               struct cpsw_slave *slave = cpsw->slaves + priv->emac_port;
-
-               cpsw_ale_add_mcast(cpsw->ale, addr, ALE_PORT_HOST,
-                                  ALE_VLAN, slave->port_vlan, 0);
-               return;
-       }
-
-       cpsw_ale_add_mcast(cpsw->ale, addr, ALE_ALL_PORTS, 0, 0, 0);
-}
-
 static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
 {
        struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
@@ -640,7 +630,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
 
                        /* Clear all mcast from ALE */
                        cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1);
-                       __dev_mc_unsync(ndev, NULL);
+                       __hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL);
 
                        /* Flood All Unicast Packets to Host port */
                        cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
@@ -661,29 +651,148 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
        }
 }
 
-static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr)
+struct addr_sync_ctx {
+       struct net_device *ndev;
+       const u8 *addr;         /* address to be synched */
+       int consumed;           /* number of address instances */
+       int flush;              /* flush flag */
+};
+
+/**
+ * cpsw_set_mc - adds multicast entry to the table if it's not added or deletes
+ * if it's not deleted
+ * @ndev: device to sync
+ * @addr: address to be added or deleted
+ * @vid: vlan id, if vid < 0 set/unset address for real device
+ * @add: add address if the flag is set or remove otherwise
+ */
+static int cpsw_set_mc(struct net_device *ndev, const u8 *addr,
+                      int vid, int add)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
+       struct cpsw_common *cpsw = priv->cpsw;
+       int mask, flags, ret;
+
+       if (vid < 0) {
+               if (cpsw->data.dual_emac)
+                       vid = cpsw->slaves[priv->emac_port].port_vlan;
+               else
+                       vid = 0;
+       }
+
+       mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS;
+       flags = vid ? ALE_VLAN : 0;
+
+       if (add)
+               ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0);
+       else
+               ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
+
+       return ret;
+}
+
+static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx)
+{
+       struct addr_sync_ctx *sync_ctx = ctx;
+       struct netdev_hw_addr *ha;
+       int found = 0, ret = 0;
+
+       if (!vdev || !(vdev->flags & IFF_UP))
+               return 0;
+
+       /* vlan address is relevant if its sync_cnt != 0 */
+       netdev_for_each_mc_addr(ha, vdev) {
+               if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
+                       found = ha->sync_cnt;
+                       break;
+               }
+       }
+
+       if (found)
+               sync_ctx->consumed++;
+
+       if (sync_ctx->flush) {
+               if (!found)
+                       cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
+               return 0;
+       }
+
+       if (found)
+               ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1);
+
+       return ret;
+}
+
+static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num)
+{
+       struct addr_sync_ctx sync_ctx;
+       int ret;
+
+       sync_ctx.consumed = 0;
+       sync_ctx.addr = addr;
+       sync_ctx.ndev = ndev;
+       sync_ctx.flush = 0;
+
+       ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
+       if (sync_ctx.consumed < num && !ret)
+               ret = cpsw_set_mc(ndev, addr, -1, 1);
+
+       return ret;
+}
+
+static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num)
+{
+       struct addr_sync_ctx sync_ctx;
+
+       sync_ctx.consumed = 0;
+       sync_ctx.addr = addr;
+       sync_ctx.ndev = ndev;
+       sync_ctx.flush = 1;
+
+       vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
+       if (sync_ctx.consumed == num)
+               cpsw_set_mc(ndev, addr, -1, 0);
 
-       cpsw_add_mcast(priv, addr);
        return 0;
 }
 
-static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr)
+static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx)
 {
-       struct cpsw_priv *priv = netdev_priv(ndev);
-       struct cpsw_common *cpsw = priv->cpsw;
-       int vid, flags;
+       struct addr_sync_ctx *sync_ctx = ctx;
+       struct netdev_hw_addr *ha;
+       int found = 0;
 
-       if (cpsw->data.dual_emac) {
-               vid = cpsw->slaves[priv->emac_port].port_vlan;
-               flags = ALE_VLAN;
-       } else {
-               vid = 0;
-               flags = 0;
+       if (!vdev || !(vdev->flags & IFF_UP))
+               return 0;
+
+       /* vlan address is relevant if its sync_cnt != 0 */
+       netdev_for_each_mc_addr(ha, vdev) {
+               if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
+                       found = ha->sync_cnt;
+                       break;
+               }
        }
 
-       cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
+       if (!found)
+               return 0;
+
+       sync_ctx->consumed++;
+       cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
+       return 0;
+}
+
+static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
+{
+       struct addr_sync_ctx sync_ctx;
+
+       sync_ctx.addr = addr;
+       sync_ctx.ndev = ndev;
+       sync_ctx.consumed = 0;
+
+       vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx);
+       if (sync_ctx.consumed < num)
+               cpsw_set_mc(ndev, addr, -1, 0);
+
        return 0;
 }
 
@@ -704,7 +813,9 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
        /* Restore allmulti on vlans if necessary */
        cpsw_ale_set_allmulti(cpsw->ale, ndev->flags & IFF_ALLMULTI);
 
-       __dev_mc_sync(ndev, cpsw_add_mc_addr, cpsw_del_mc_addr);
+       /* add/remove mcast address either for real netdev or for vlan */
+       __hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
+                              cpsw_del_mc_addr);
 }
 
 static void cpsw_intr_enable(struct cpsw_common *cpsw)
@@ -796,6 +907,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
        struct net_device       *ndev = skb->dev;
        int                     ret = 0, port;
        struct cpsw_common      *cpsw = ndev_to_cpsw(ndev);
+       struct cpsw_priv        *priv;
 
        if (cpsw->data.dual_emac) {
                port = CPDMA_RX_SOURCE_PORT(status);
@@ -830,7 +942,9 @@ static void cpsw_rx_handler(void *token, int len, int status)
                skb_put(skb, len);
                if (status & CPDMA_RX_VLAN_ENCAP)
                        cpsw_rx_vlan_encap(skb);
-               cpts_rx_timestamp(cpsw->cpts, skb);
+               priv = netdev_priv(ndev);
+               if (priv->rx_ts_enabled)
+                       cpts_rx_timestamp(cpsw->cpts, skb);
                skb->protocol = eth_type_trans(skb, ndev);
                netif_receive_skb(skb);
                ndev->stats.rx_bytes += len;
@@ -1845,9 +1959,23 @@ static void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv)
        slave_write(slave, tx_prio_map, tx_prio_rg);
 }
 
+static int cpsw_restore_vlans(struct net_device *vdev, int vid, void *arg)
+{
+       struct cpsw_priv *priv = arg;
+
+       if (!vdev)
+               return 0;
+
+       cpsw_ndo_vlan_rx_add_vid(priv->ndev, 0, vid);
+       return 0;
+}
+
 /* restore resources after port reset */
 static void cpsw_restore(struct cpsw_priv *priv)
 {
+       /* restore vlan configurations */
+       vlan_for_each(priv->ndev, cpsw_restore_vlans, priv);
+
        /* restore MQPRIO offload */
        for_each_slave(priv, cpsw_mqprio_resume, priv);
 
@@ -1964,7 +2092,7 @@ static int cpsw_ndo_stop(struct net_device *ndev)
        struct cpsw_common *cpsw = priv->cpsw;
 
        cpsw_info(priv, ifdown, "shutting down cpsw device\n");
-       __dev_mc_unsync(priv->ndev, cpsw_del_mc_addr);
+       __hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc);
        netif_tx_stop_all_queues(priv->ndev);
        netif_carrier_off(priv->ndev);
 
@@ -2003,7 +2131,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
        }
 
        if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-           cpts_is_tx_enabled(cpts) && cpts_can_timestamp(cpts, skb))
+           priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
                skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
        q_idx = skb_get_queue_mapping(skb);
@@ -2047,13 +2175,13 @@ fail:
 
 #if IS_ENABLED(CONFIG_TI_CPTS)
 
-static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
+static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 {
+       struct cpsw_common *cpsw = priv->cpsw;
        struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
        u32 ts_en, seq_id;
 
-       if (!cpts_is_tx_enabled(cpsw->cpts) &&
-           !cpts_is_rx_enabled(cpsw->cpts)) {
+       if (!priv->tx_ts_enabled && !priv->rx_ts_enabled) {
                slave_write(slave, 0, CPSW1_TS_CTL);
                return;
        }
@@ -2061,10 +2189,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
        seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
        ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
 
-       if (cpts_is_tx_enabled(cpsw->cpts))
+       if (priv->tx_ts_enabled)
                ts_en |= CPSW_V1_TS_TX_EN;
 
-       if (cpts_is_rx_enabled(cpsw->cpts))
+       if (priv->rx_ts_enabled)
                ts_en |= CPSW_V1_TS_RX_EN;
 
        slave_write(slave, ts_en, CPSW1_TS_CTL);
@@ -2084,20 +2212,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        case CPSW_VERSION_2:
                ctrl &= ~CTRL_V2_ALL_TS_MASK;
 
-               if (cpts_is_tx_enabled(cpsw->cpts))
+               if (priv->tx_ts_enabled)
                        ctrl |= CTRL_V2_TX_TS_BITS;
 
-               if (cpts_is_rx_enabled(cpsw->cpts))
+               if (priv->rx_ts_enabled)
                        ctrl |= CTRL_V2_RX_TS_BITS;
                break;
        case CPSW_VERSION_3:
        default:
                ctrl &= ~CTRL_V3_ALL_TS_MASK;
 
-               if (cpts_is_tx_enabled(cpsw->cpts))
+               if (priv->tx_ts_enabled)
                        ctrl |= CTRL_V3_TX_TS_BITS;
 
-               if (cpts_is_rx_enabled(cpsw->cpts))
+               if (priv->rx_ts_enabled)
                        ctrl |= CTRL_V3_RX_TS_BITS;
                break;
        }
@@ -2107,6 +2235,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE);
        slave_write(slave, ctrl, CPSW2_CONTROL);
        writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype);
+       writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype);
 }
 
 static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
@@ -2114,7 +2243,6 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        struct cpsw_priv *priv = netdev_priv(dev);
        struct hwtstamp_config cfg;
        struct cpsw_common *cpsw = priv->cpsw;
-       struct cpts *cpts = cpsw->cpts;
 
        if (cpsw->version != CPSW_VERSION_1 &&
            cpsw->version != CPSW_VERSION_2 &&
@@ -2133,7 +2261,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
-               cpts_rx_enable(cpts, 0);
+               priv->rx_ts_enabled = 0;
                break;
        case HWTSTAMP_FILTER_ALL:
        case HWTSTAMP_FILTER_NTP_ALL:
@@ -2141,7 +2269,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+               priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2153,18 +2281,18 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+               priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        default:
                return -ERANGE;
        }
 
-       cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON);
+       priv->tx_ts_enabled = cfg.tx_type == HWTSTAMP_TX_ON;
 
        switch (cpsw->version) {
        case CPSW_VERSION_1:
-               cpsw_hwtstamp_v1(cpsw);
+               cpsw_hwtstamp_v1(priv);
                break;
        case CPSW_VERSION_2:
        case CPSW_VERSION_3:
@@ -2180,7 +2308,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
 {
        struct cpsw_common *cpsw = ndev_to_cpsw(dev);
-       struct cpts *cpts = cpsw->cpts;
+       struct cpsw_priv *priv = netdev_priv(dev);
        struct hwtstamp_config cfg;
 
        if (cpsw->version != CPSW_VERSION_1 &&
@@ -2189,10 +2317,8 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
-       cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-                     HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-                        cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+       cfg.tx_type = priv->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = priv->rx_ts_enabled;
 
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
@@ -2415,6 +2541,7 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
                                  HOST_PORT_NUM, ALE_VLAN, vid);
        ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
                                  0, ALE_VLAN, vid);
+       ret |= cpsw_ale_flush_multicast(cpsw->ale, 0, vid);
 err:
        pm_runtime_put(cpsw->dev);
        return ret;
@@ -3144,7 +3271,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                const __be32 *parp;
 
                /* This is no slave child node, continue */
-               if (strcmp(slave_node->name, "slave"))
+               if (!of_node_name_eq(slave_node, "slave"))
                        continue;
 
                slave_data->phy_node = of_parse_phandle(slave_node,
@@ -3240,7 +3367,7 @@ static void cpsw_remove_dt(struct platform_device *pdev)
        for_each_available_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = &data->slave_data[i];
 
-               if (strcmp(slave_node->name, "slave"))
+               if (!of_node_name_eq(slave_node, "slave"))
                        continue;
 
                if (of_phy_is_fixed_link(slave_node))
index b96b93c..054f782 100644 (file)
@@ -86,6 +86,25 @@ static int cpts_purge_events(struct cpts *cpts)
        return removed ? 0 : -1;
 }
 
+static void cpts_purge_txq(struct cpts *cpts)
+{
+       struct cpts_skb_cb_data *skb_cb;
+       struct sk_buff *skb, *tmp;
+       int removed = 0;
+
+       skb_queue_walk_safe(&cpts->txq, skb, tmp) {
+               skb_cb = (struct cpts_skb_cb_data *)skb->cb;
+               if (time_after(jiffies, skb_cb->tmo)) {
+                       __skb_unlink(skb, &cpts->txq);
+                       dev_consume_skb_any(skb);
+                       ++removed;
+               }
+       }
+
+       if (removed)
+               dev_dbg(cpts->dev, "txq cleaned up %d\n", removed);
+}
+
 static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
 {
        struct sk_buff *skb, *tmp;
@@ -119,9 +138,7 @@ static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
 
                if (time_after(jiffies, skb_cb->tmo)) {
                        /* timeout any expired skbs over 1s */
-                       dev_dbg(cpts->dev,
-                               "expiring tx timestamp mtype %u seqid %04x\n",
-                               mtype, seqid);
+                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
                        __skb_unlink(skb, &cpts->txq);
                        dev_consume_skb_any(skb);
                }
@@ -294,8 +311,11 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp)
        spin_lock_irqsave(&cpts->lock, flags);
        ts = ns_to_timespec64(timecounter_read(&cpts->tc));
 
-       if (!skb_queue_empty(&cpts->txq))
-               delay = CPTS_SKB_TX_WORK_TIMEOUT;
+       if (!skb_queue_empty(&cpts->txq)) {
+               cpts_purge_txq(cpts);
+               if (!skb_queue_empty(&cpts->txq))
+                       delay = CPTS_SKB_TX_WORK_TIMEOUT;
+       }
        spin_unlock_irqrestore(&cpts->lock, flags);
 
        pr_debug("cpts overflow check at %lld.%09ld\n",
@@ -410,8 +430,6 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
        u64 ns;
        struct skb_shared_hwtstamps *ssh;
 
-       if (!cpts->rx_enable)
-               return;
        ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
        if (!ns)
                return;
index 73d73fa..d2c7dec 100644 (file)
@@ -136,26 +136,6 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
                         struct device_node *node);
 void cpts_release(struct cpts *cpts);
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-       cpts->rx_enable = enable;
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-       return !!cpts->rx_enable;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-       cpts->tx_enable = enable;
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-       return !!cpts->tx_enable;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        unsigned int class = ptp_classify_raw(skb);
@@ -197,24 +177,6 @@ static inline void cpts_unregister(struct cpts *cpts)
 {
 }
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-       return false;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-       return false;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        return false;
index 9153db1..8408204 100644 (file)
@@ -1912,11 +1912,15 @@ static int davinci_emac_probe(struct platform_device *pdev)
                ether_addr_copy(ndev->dev_addr, priv->mac_addr);
 
        if (!is_valid_ether_addr(priv->mac_addr)) {
-               /* Use random MAC if none passed */
-               eth_hw_addr_random(ndev);
-               memcpy(priv->mac_addr, ndev->dev_addr, ndev->addr_len);
-               dev_warn(&pdev->dev, "using random MAC addr: %pM\n",
-                                                       priv->mac_addr);
+               /* Try nvmem if MAC wasn't passed over pdata or DT. */
+               rc = nvmem_get_mac_address(&pdev->dev, priv->mac_addr);
+               if (rc) {
+                       /* Use random MAC if still none obtained. */
+                       eth_hw_addr_random(ndev);
+                       memcpy(priv->mac_addr, ndev->dev_addr, ndev->addr_len);
+                       dev_warn(&pdev->dev, "using random MAC addr: %pM\n",
+                                priv->mac_addr);
+               }
        }
 
        ndev->netdev_ops = &emac_netdev_ops;
index 0397ccb..5174d31 100644 (file)
@@ -763,6 +763,8 @@ struct gbe_priv {
 
        int                             cpts_registered;
        struct cpts                     *cpts;
+       int                             rx_ts_enabled;
+       int                             tx_ts_enabled;
 };
 
 struct gbe_intf {
@@ -2564,7 +2566,7 @@ static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf,
        struct gbe_priv *gbe_dev = gbe_intf->gbe_dev;
 
        if (!(skb_shinfo(p_info->skb)->tx_flags & SKBTX_HW_TSTAMP) ||
-           !cpts_is_tx_enabled(gbe_dev->cpts))
+           !gbe_dev->tx_ts_enabled)
                return 0;
 
        /* If phy has the txtstamp api, assume it will do it.
@@ -2598,7 +2600,9 @@ static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info)
                return 0;
        }
 
-       cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+       if (gbe_dev->rx_ts_enabled)
+               cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+
        p_info->rxtstamp_complete = true;
 
        return 0;
@@ -2614,10 +2618,8 @@ static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
-       cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-                     HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-                        cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+       cfg.tx_type = gbe_dev->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = gbe_dev->rx_ts_enabled;
 
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
@@ -2628,8 +2630,8 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
        struct gbe_slave *slave = gbe_intf->slave;
        u32 ts_en, seq_id, ctl;
 
-       if (!cpts_is_rx_enabled(gbe_dev->cpts) &&
-           !cpts_is_tx_enabled(gbe_dev->cpts)) {
+       if (!gbe_dev->rx_ts_enabled &&
+           !gbe_dev->tx_ts_enabled) {
                writel(0, GBE_REG_ADDR(slave, port_regs, ts_ctl));
                return;
        }
@@ -2641,10 +2643,10 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
                (slave->ts_ctl.uni ?  TS_UNI_EN :
                        slave->ts_ctl.maddr_map << TS_CTL_MADDR_SHIFT);
 
-       if (cpts_is_tx_enabled(gbe_dev->cpts))
+       if (gbe_dev->tx_ts_enabled)
                ts_en |= (TS_TX_ANX_ALL_EN | TS_TX_VLAN_LT1_EN);
 
-       if (cpts_is_rx_enabled(gbe_dev->cpts))
+       if (gbe_dev->rx_ts_enabled)
                ts_en |= (TS_RX_ANX_ALL_EN | TS_RX_VLAN_LT1_EN);
 
        writel(ts_en,  GBE_REG_ADDR(slave, port_regs, ts_ctl));
@@ -2670,10 +2672,10 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
        switch (cfg.tx_type) {
        case HWTSTAMP_TX_OFF:
-               cpts_tx_enable(cpts, 0);
+               gbe_dev->tx_ts_enabled = 0;
                break;
        case HWTSTAMP_TX_ON:
-               cpts_tx_enable(cpts, 1);
+               gbe_dev->tx_ts_enabled = 1;
                break;
        default:
                return -ERANGE;
@@ -2681,12 +2683,12 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
        switch (cfg.rx_filter) {
        case HWTSTAMP_FILTER_NONE:
-               cpts_rx_enable(cpts, 0);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_NONE;
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
        case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2698,7 +2700,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_EVENT:
        case HWTSTAMP_FILTER_PTP_V2_SYNC:
        case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-               cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+               gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
                cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        default:
@@ -3621,7 +3623,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
                return -EINVAL;
        }
 
-       if (!strcmp(node->name, "gbe")) {
+       if (of_node_name_eq(node, "gbe")) {
                ret = get_gbe_resource_version(gbe_dev, node);
                if (ret)
                        return ret;
@@ -3635,7 +3637,7 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
                else
                        ret = -ENODEV;
 
-       } else if (!strcmp(node->name, "xgbe")) {
+       } else if (of_node_name_eq(node, "xgbe")) {
                ret = set_xgbe_ethss10_priv(gbe_dev, node);
                if (ret)
                        return ret;
index 93d1428..b4ab1a5 100644 (file)
@@ -69,7 +69,9 @@ MODULE_AUTHOR("Maintainer: Samuel Chessman <chessman@tux.org>");
 MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
 MODULE_LICENSE("GPL");
 
-/* Turn on debugging. See Documentation/networking/tlan.txt for details */
+/* Turn on debugging.
+ * See Documentation/networking/device_drivers/ti/tlan.txt for details
+ */
 static  int            debug;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, "ThunderLAN debug mask");
index 6a71c2c..c50a977 100644 (file)
@@ -607,9 +607,9 @@ static void tc_handle_link_change(struct net_device *dev)
 
 static int tc_mii_probe(struct net_device *dev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
        struct tc35815_local *lp = netdev_priv(dev);
        struct phy_device *phydev;
-       u32 dropmask;
 
        phydev = phy_find_first(lp->mii_bus);
        if (!phydev) {
@@ -630,17 +630,22 @@ static int tc_mii_probe(struct net_device *dev)
 
        /* mask with MAC supported features */
        phy_set_max_speed(phydev, SPEED_100);
-       dropmask = 0;
-       if (options.speed == 10)
-               dropmask |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
-       else if (options.speed == 100)
-               dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full;
-       if (options.duplex == 1)
-               dropmask |= SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full;
-       else if (options.duplex == 2)
-               dropmask |= SUPPORTED_10baseT_Half | SUPPORTED_100baseT_Half;
-       phydev->supported &= ~dropmask;
-       phydev->advertising = phydev->supported;
+       if (options.speed == 10) {
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
+       } else if (options.speed == 100) {
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, mask);
+       }
+       if (options.duplex == 1) {
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
+       } else if (options.duplex == 2) {
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
+       }
+       linkmode_and(phydev->supported, phydev->supported, mask);
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        lp->link = 0;
        lp->speed = 0;
index a0cd1c4..58bbba8 100644 (file)
@@ -70,6 +70,7 @@ struct geneve_dev {
        bool               collect_md;
        bool               use_udp6_rx_checksums;
        bool               ttl_inherit;
+       enum ifla_geneve_df df;
 };
 
 struct geneve_sock {
@@ -387,6 +388,59 @@ drop:
        return 0;
 }
 
+/* Callback from net/ipv{4,6}/udp.c to check that we have a tunnel for errors */
+static int geneve_udp_encap_err_lookup(struct sock *sk, struct sk_buff *skb)
+{
+       struct genevehdr *geneveh;
+       struct geneve_sock *gs;
+       u8 zero_vni[3] = { 0 };
+       u8 *vni = zero_vni;
+
+       if (skb->len < GENEVE_BASE_HLEN)
+               return -EINVAL;
+
+       geneveh = geneve_hdr(skb);
+       if (geneveh->ver != GENEVE_VER)
+               return -EINVAL;
+
+       if (geneveh->proto_type != htons(ETH_P_TEB))
+               return -EINVAL;
+
+       gs = rcu_dereference_sk_user_data(sk);
+       if (!gs)
+               return -ENOENT;
+
+       if (geneve_get_sk_family(gs) == AF_INET) {
+               struct iphdr *iph = ip_hdr(skb);
+               __be32 addr4 = 0;
+
+               if (!gs->collect_md) {
+                       vni = geneve_hdr(skb)->vni;
+                       addr4 = iph->daddr;
+               }
+
+               return geneve_lookup(gs, addr4, vni) ? 0 : -ENOENT;
+       }
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if (geneve_get_sk_family(gs) == AF_INET6) {
+               struct ipv6hdr *ip6h = ipv6_hdr(skb);
+               struct in6_addr addr6;
+
+               memset(&addr6, 0, sizeof(struct in6_addr));
+
+               if (!gs->collect_md) {
+                       vni = geneve_hdr(skb)->vni;
+                       addr6 = ip6h->daddr;
+               }
+
+               return geneve6_lookup(gs, addr6, vni) ? 0 : -ENOENT;
+       }
+#endif
+
+       return -EPFNOSUPPORT;
+}
+
 static struct socket *geneve_create_sock(struct net *net, bool ipv6,
                                         __be16 port, bool ipv6_rx_csum)
 {
@@ -544,6 +598,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
        tunnel_cfg.gro_receive = geneve_gro_receive;
        tunnel_cfg.gro_complete = geneve_gro_complete;
        tunnel_cfg.encap_rcv = geneve_udp_encap_recv;
+       tunnel_cfg.encap_err_lookup = geneve_udp_encap_err_lookup;
        tunnel_cfg.encap_destroy = NULL;
        setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
        list_add(&gs->list, &gn->sock_list);
@@ -823,8 +878,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        struct rtable *rt;
        struct flowi4 fl4;
        __u8 tos, ttl;
+       __be16 df = 0;
        __be16 sport;
-       __be16 df;
        int err;
 
        rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info);
@@ -838,6 +893,8 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
        if (geneve->collect_md) {
                tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
                ttl = key->ttl;
+
+               df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
        } else {
                tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb);
                if (geneve->ttl_inherit)
@@ -845,8 +902,22 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                else
                        ttl = key->ttl;
                ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
+
+               if (geneve->df == GENEVE_DF_SET) {
+                       df = htons(IP_DF);
+               } else if (geneve->df == GENEVE_DF_INHERIT) {
+                       struct ethhdr *eth = eth_hdr(skb);
+
+                       if (ntohs(eth->h_proto) == ETH_P_IPV6) {
+                               df = htons(IP_DF);
+                       } else if (ntohs(eth->h_proto) == ETH_P_IP) {
+                               struct iphdr *iph = ip_hdr(skb);
+
+                               if (iph->frag_off & htons(IP_DF))
+                                       df = htons(IP_DF);
+                       }
+               }
        }
-       df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
 
        err = geneve_build_skb(&rt->dst, skb, info, xnet, sizeof(struct iphdr));
        if (unlikely(err))
@@ -1093,6 +1164,7 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
        [IFLA_GENEVE_TTL_INHERIT]       = { .type = NLA_U8 },
+       [IFLA_GENEVE_DF]                = { .type = NLA_U8 },
 };
 
 static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1128,6 +1200,16 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
                }
        }
 
+       if (data[IFLA_GENEVE_DF]) {
+               enum ifla_geneve_df df = nla_get_u8(data[IFLA_GENEVE_DF]);
+
+               if (df < 0 || df > GENEVE_DF_MAX) {
+                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_GENEVE_DF],
+                                           "Invalid DF attribute");
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 }
 
@@ -1173,7 +1255,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
                            struct netlink_ext_ack *extack,
                            const struct ip_tunnel_info *info,
                            bool metadata, bool ipv6_rx_csum,
-                           bool ttl_inherit)
+                           bool ttl_inherit, enum ifla_geneve_df df)
 {
        struct geneve_net *gn = net_generic(net, geneve_net_id);
        struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -1223,6 +1305,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
        geneve->collect_md = metadata;
        geneve->use_udp6_rx_checksums = ipv6_rx_csum;
        geneve->ttl_inherit = ttl_inherit;
+       geneve->df = df;
 
        err = register_netdevice(dev);
        if (err)
@@ -1242,7 +1325,7 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
                          struct netlink_ext_ack *extack,
                          struct ip_tunnel_info *info, bool *metadata,
                          bool *use_udp6_rx_checksums, bool *ttl_inherit,
-                         bool changelink)
+                         enum ifla_geneve_df *df, bool changelink)
 {
        int attrtype;
 
@@ -1330,6 +1413,9 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
        if (data[IFLA_GENEVE_TOS])
                info->key.tos = nla_get_u8(data[IFLA_GENEVE_TOS]);
 
+       if (data[IFLA_GENEVE_DF])
+               *df = nla_get_u8(data[IFLA_GENEVE_DF]);
+
        if (data[IFLA_GENEVE_LABEL]) {
                info->key.label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
                                  IPV6_FLOWLABEL_MASK;
@@ -1448,6 +1534,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
                          struct nlattr *tb[], struct nlattr *data[],
                          struct netlink_ext_ack *extack)
 {
+       enum ifla_geneve_df df = GENEVE_DF_UNSET;
        bool use_udp6_rx_checksums = false;
        struct ip_tunnel_info info;
        bool ttl_inherit = false;
@@ -1456,12 +1543,12 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 
        init_tnl_info(&info, GENEVE_UDP_PORT);
        err = geneve_nl2info(tb, data, extack, &info, &metadata,
-                            &use_udp6_rx_checksums, &ttl_inherit, false);
+                            &use_udp6_rx_checksums, &ttl_inherit, &df, false);
        if (err)
                return err;
 
        err = geneve_configure(net, dev, extack, &info, metadata,
-                              use_udp6_rx_checksums, ttl_inherit);
+                              use_udp6_rx_checksums, ttl_inherit, df);
        if (err)
                return err;
 
@@ -1524,6 +1611,7 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[],
        struct ip_tunnel_info info;
        bool metadata;
        bool use_udp6_rx_checksums;
+       enum ifla_geneve_df df;
        bool ttl_inherit;
        int err;
 
@@ -1539,7 +1627,7 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[],
        use_udp6_rx_checksums = geneve->use_udp6_rx_checksums;
        ttl_inherit = geneve->ttl_inherit;
        err = geneve_nl2info(tb, data, extack, &info, &metadata,
-                            &use_udp6_rx_checksums, &ttl_inherit, true);
+                            &use_udp6_rx_checksums, &ttl_inherit, &df, true);
        if (err)
                return err;
 
@@ -1572,6 +1660,7 @@ static size_t geneve_get_size(const struct net_device *dev)
                nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
+               nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_DF */
                nla_total_size(sizeof(__be32)) +  /* IFLA_GENEVE_LABEL */
                nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
                nla_total_size(0) +      /* IFLA_GENEVE_COLLECT_METADATA */
@@ -1620,6 +1709,9 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
            nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label))
                goto nla_put_failure;
 
+       if (nla_put_u8(skb, IFLA_GENEVE_DF, geneve->df))
+               goto nla_put_failure;
+
        if (nla_put_be16(skb, IFLA_GENEVE_PORT, info->key.tp_dst))
                goto nla_put_failure;
 
@@ -1666,12 +1758,13 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 
        memset(tb, 0, sizeof(tb));
        dev = rtnl_create_link(net, name, name_assign_type,
-                              &geneve_link_ops, tb);
+                              &geneve_link_ops, tb, NULL);
        if (IS_ERR(dev))
                return dev;
 
        init_tnl_info(&info, dst_port);
-       err = geneve_configure(net, dev, NULL, &info, true, true, false);
+       err = geneve_configure(net, dev, NULL, &info,
+                              true, true, false, GENEVE_DF_UNSET);
        if (err) {
                free_netdev(dev);
                return ERR_PTR(err);
index cf36e7f..18b5584 100644 (file)
@@ -137,7 +137,7 @@ static int netvsc_open(struct net_device *net)
                 * slave as up. If open fails, then slave will be
                 * still be offline (and not used).
                 */
-               ret = dev_open(vf_netdev);
+               ret = dev_open(vf_netdev, NULL);
                if (ret)
                        netdev_warn(net,
                                    "unable to open slave: %s: %d\n",
@@ -605,9 +605,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                     IEEE_8021Q_INFO);
 
                vlan->value = 0;
-               vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK;
-               vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >>
-                               VLAN_PRIO_SHIFT;
+               vlan->vlanid = skb_vlan_tag_get_id(skb);
+               vlan->cfi = skb_vlan_tag_get_cfi(skb);
+               vlan->pri = skb_vlan_tag_get_prio(skb);
        }
 
        if (skb_is_gso(skb)) {
@@ -781,7 +781,8 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
        }
 
        if (vlan) {
-               u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT);
+               u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
+                       (vlan->cfi ? VLAN_CFI_MASK : 0);
 
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                       vlan_tci);
@@ -1992,7 +1993,7 @@ static void __netvsc_vf_setup(struct net_device *ndev,
                            "unable to change mtu to %u\n", ndev->mtu);
 
        /* set multicast etc flags on VF */
-       dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE);
+       dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE, NULL);
 
        /* sync address list from ndev to VF */
        netif_addr_lock_bh(ndev);
@@ -2001,7 +2002,7 @@ static void __netvsc_vf_setup(struct net_device *ndev,
        netif_addr_unlock_bh(ndev);
 
        if (netif_running(ndev)) {
-               ret = dev_open(vf_netdev);
+               ret = dev_open(vf_netdev, NULL);
                if (ret)
                        netdev_warn(vf_netdev,
                                    "unable to open: %d\n", ret);
index 4a94956..c3d3e45 100644 (file)
@@ -71,7 +71,8 @@ static void ipvlan_unregister_nf_hook(struct net *net)
                                        ARRAY_SIZE(ipvl_nfops));
 }
 
-static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
+static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
+                               struct netlink_ext_ack *extack)
 {
        struct ipvl_dev *ipvlan;
        struct net_device *mdev = port->dev;
@@ -84,10 +85,12 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval)
                        flags = ipvlan->dev->flags;
                        if (nval == IPVLAN_MODE_L3 || nval == IPVLAN_MODE_L3S) {
                                err = dev_change_flags(ipvlan->dev,
-                                                      flags | IFF_NOARP);
+                                                      flags | IFF_NOARP,
+                                                      extack);
                        } else {
                                err = dev_change_flags(ipvlan->dev,
-                                                      flags & ~IFF_NOARP);
+                                                      flags & ~IFF_NOARP,
+                                                      extack);
                        }
                        if (unlikely(err))
                                goto fail;
@@ -116,9 +119,11 @@ fail:
                flags = ipvlan->dev->flags;
                if (port->mode == IPVLAN_MODE_L3 ||
                    port->mode == IPVLAN_MODE_L3S)
-                       dev_change_flags(ipvlan->dev, flags | IFF_NOARP);
+                       dev_change_flags(ipvlan->dev, flags | IFF_NOARP,
+                                        NULL);
                else
-                       dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP);
+                       dev_change_flags(ipvlan->dev, flags & ~IFF_NOARP,
+                                        NULL);
        }
 
        return err;
@@ -498,7 +503,7 @@ static int ipvlan_nl_changelink(struct net_device *dev,
        if (data[IFLA_IPVLAN_MODE]) {
                u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
 
-               err = ipvlan_set_port_mode(port, nmode);
+               err = ipvlan_set_port_mode(port, nmode, extack);
        }
 
        if (!err && data[IFLA_IPVLAN_FLAGS]) {
@@ -672,7 +677,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev,
        if (data && data[IFLA_IPVLAN_MODE])
                mode = nla_get_u16(data[IFLA_IPVLAN_MODE]);
 
-       err = ipvlan_set_port_mode(port, mode);
+       err = ipvlan_set_port_mode(port, mode, extack);
        if (err)
                goto unlink_netdev;
 
index e964d31..ed1166a 100644 (file)
@@ -40,14 +40,14 @@ static int net_failover_open(struct net_device *dev)
 
        primary_dev = rtnl_dereference(nfo_info->primary_dev);
        if (primary_dev) {
-               err = dev_open(primary_dev);
+               err = dev_open(primary_dev, NULL);
                if (err)
                        goto err_primary_open;
        }
 
        standby_dev = rtnl_dereference(nfo_info->standby_dev);
        if (standby_dev) {
-               err = dev_open(standby_dev);
+               err = dev_open(standby_dev, NULL);
                if (err)
                        goto err_standby_open;
        }
@@ -517,7 +517,7 @@ static int net_failover_slave_register(struct net_device *slave_dev,
        dev_hold(slave_dev);
 
        if (netif_running(failover_dev)) {
-               err = dev_open(slave_dev);
+               err = dev_open(slave_dev, NULL);
                if (err && (err != -EBUSY)) {
                        netdev_err(failover_dev, "Opening slave %s failed err:%d\n",
                                   slave_dev->name, err);
@@ -680,7 +680,7 @@ static int net_failover_slave_name_change(struct net_device *slave_dev,
        /* We need to bring up the slave after the rename by udev in case
         * open failed with EBUSY when it was registered.
         */
-       dev_open(slave_dev);
+       dev_open(slave_dev, NULL);
 
        return 0;
 }
index cb35184..a1b2917 100644 (file)
@@ -91,11 +91,6 @@ static int nsim_bpf_finalize(struct bpf_verifier_env *env)
        return 0;
 }
 
-static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = {
-       .insn_hook      = nsim_bpf_verify_insn,
-       .finalize       = nsim_bpf_finalize,
-};
-
 static bool nsim_xdp_offload_active(struct netdevsim *ns)
 {
        return ns->xdp_hw.prog;
@@ -263,6 +258,24 @@ static int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog)
        return 0;
 }
 
+static int nsim_bpf_verifier_prep(struct bpf_prog *prog)
+{
+       struct netdevsim *ns = netdev_priv(prog->aux->offload->netdev);
+
+       if (!ns->bpf_bind_accept)
+               return -EOPNOTSUPP;
+
+       return nsim_bpf_create_prog(ns, prog);
+}
+
+static int nsim_bpf_translate(struct bpf_prog *prog)
+{
+       struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv;
+
+       state->state = "xlated";
+       return 0;
+}
+
 static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
 {
        struct nsim_bpf_bound_prog *state;
@@ -275,6 +288,14 @@ static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
        kfree(state);
 }
 
+static const struct bpf_prog_offload_ops nsim_bpf_dev_ops = {
+       .insn_hook      = nsim_bpf_verify_insn,
+       .finalize       = nsim_bpf_finalize,
+       .prepare        = nsim_bpf_verifier_prep,
+       .translate      = nsim_bpf_translate,
+       .destroy        = nsim_bpf_destroy_prog,
+};
+
 static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
 {
        if (bpf->prog && bpf->prog->aux->offload) {
@@ -533,30 +554,11 @@ static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap)
 int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
 {
        struct netdevsim *ns = netdev_priv(dev);
-       struct nsim_bpf_bound_prog *state;
        int err;
 
        ASSERT_RTNL();
 
        switch (bpf->command) {
-       case BPF_OFFLOAD_VERIFIER_PREP:
-               if (!ns->bpf_bind_accept)
-                       return -EOPNOTSUPP;
-
-               err = nsim_bpf_create_prog(ns, bpf->verifier.prog);
-               if (err)
-                       return err;
-
-               bpf->verifier.ops = &nsim_bpf_analyzer_ops;
-               return 0;
-       case BPF_OFFLOAD_TRANSLATE:
-               state = bpf->offload.prog->aux->offload->dev_priv;
-
-               state->state = "xlated";
-               return 0;
-       case BPF_OFFLOAD_DESTROY:
-               nsim_bpf_destroy_prog(bpf->offload.prog);
-               return 0;
        case XDP_QUERY_PROG:
                return xdp_attachment_query(&ns->xdp, bpf);
        case XDP_QUERY_PROG_HW:
@@ -599,7 +601,7 @@ int nsim_bpf_init(struct netdevsim *ns)
                if (IS_ERR_OR_NULL(ns->sdev->ddir_bpf_bound_progs))
                        return -ENOMEM;
 
-               ns->sdev->bpf_dev = bpf_offload_dev_create();
+               ns->sdev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops);
                err = PTR_ERR_OR_ZERO(ns->sdev->bpf_dev);
                if (err)
                        return err;
index 6fe5dc9..9d0504f 100644 (file)
@@ -66,7 +66,6 @@ static struct phy_driver am79c_driver[] = { {
        .name           = "AM79C874",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = am79c_config_init,
        .ack_interrupt  = am79c_ack_interrupt,
        .config_intr    = am79c_config_intr,
index 632472c..beb3309 100644 (file)
 #define PHY_ID_AQR107  0x03a1b4e0
 #define PHY_ID_AQR405  0x03a1b4b0
 
-#define PHY_AQUANTIA_FEATURES  (SUPPORTED_10000baseT_Full | \
-                                SUPPORTED_1000baseT_Full | \
-                                SUPPORTED_100baseT_Full | \
-                                PHY_DEFAULT_FEATURES)
-
 static int aquantia_config_aneg(struct phy_device *phydev)
 {
-       phydev->supported = PHY_AQUANTIA_FEATURES;
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->supported, phy_10gbit_features);
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        return 0;
 }
@@ -116,7 +111,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQ1202",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
@@ -128,7 +122,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQ2104",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
@@ -140,7 +133,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQR105",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
@@ -152,7 +144,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQR106",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
@@ -164,7 +155,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQR107",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
@@ -176,7 +166,6 @@ static struct phy_driver aquantia_driver[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Aquantia AQR405",
        .features       = PHY_10GBIT_FULL_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .aneg_done      = genphy_c45_aneg_done,
        .config_aneg    = aquantia_config_aneg,
        .config_intr    = aquantia_config_intr,
index e74a047..f9432d0 100644 (file)
@@ -379,7 +379,6 @@ static struct phy_driver at803x_driver[] = {
        .suspend                = at803x_suspend,
        .resume                 = at803x_resume,
        .features               = PHY_GBIT_FEATURES,
-       .flags                  = PHY_HAS_INTERRUPT,
        .ack_interrupt          = at803x_ack_interrupt,
        .config_intr            = at803x_config_intr,
 }, {
@@ -395,7 +394,6 @@ static struct phy_driver at803x_driver[] = {
        .suspend                = at803x_suspend,
        .resume                 = at803x_resume,
        .features               = PHY_BASIC_FEATURES,
-       .flags                  = PHY_HAS_INTERRUPT,
        .ack_interrupt          = at803x_ack_interrupt,
        .config_intr            = at803x_config_intr,
 }, {
@@ -410,7 +408,6 @@ static struct phy_driver at803x_driver[] = {
        .suspend                = at803x_suspend,
        .resume                 = at803x_resume,
        .features               = PHY_GBIT_FEATURES,
-       .flags                  = PHY_HAS_INTERRUPT,
        .aneg_done              = at803x_aneg_done,
        .ack_interrupt          = &at803x_ack_interrupt,
        .config_intr            = &at803x_config_intr,
index d95bffd..a88dd14 100644 (file)
@@ -43,7 +43,7 @@ static int bcm63xx_config_init(struct phy_device *phydev)
        int reg, err;
 
        /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */
-       phydev->supported |= SUPPORTED_Pause;
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
 
        reg = phy_read(phydev, MII_BCM63XX_IR);
        if (reg < 0)
@@ -69,7 +69,7 @@ static struct phy_driver bcm63xx_driver[] = {
        .phy_id_mask    = 0xfffffc00,
        .name           = "Broadcom BCM63XX (1)",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL,
+       .flags          = PHY_IS_INTERNAL,
        .config_init    = bcm63xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm63xx_config_intr,
@@ -78,7 +78,7 @@ static struct phy_driver bcm63xx_driver[] = {
        .phy_id         = 0x002bdc00,
        .phy_id_mask    = 0xfffffc00,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL,
+       .flags          = PHY_IS_INTERNAL,
        .config_init    = bcm63xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm63xx_config_intr,
index b2b6307..712224c 100644 (file)
@@ -650,6 +650,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
 
 static struct phy_driver bcm7xxx_driver[] = {
        BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
+       BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"),
        BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"),
        BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"),
        BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"),
@@ -670,6 +671,7 @@ static struct phy_driver bcm7xxx_driver[] = {
 
 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
        { PHY_ID_BCM7250, 0xfffffff0, },
+       { PHY_ID_BCM7255, 0xfffffff0, },
        { PHY_ID_BCM7260, 0xfffffff0, },
        { PHY_ID_BCM7268, 0xfffffff0, },
        { PHY_ID_BCM7271, 0xfffffff0, },
index f7ebdcf..1b35018 100644 (file)
@@ -86,8 +86,12 @@ static int bcm87xx_of_reg_init(struct phy_device *phydev)
 
 static int bcm87xx_config_init(struct phy_device *phydev)
 {
-       phydev->supported = SUPPORTED_10000baseR_FEC;
-       phydev->advertising = ADVERTISED_10000baseR_FEC;
+       linkmode_zero(phydev->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+                        phydev->supported);
+       linkmode_zero(phydev->advertising);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+                        phydev->advertising);
        phydev->state = PHY_NOLINK;
        phydev->autoneg = AUTONEG_DISABLE;
 
@@ -193,7 +197,6 @@ static struct phy_driver bcm87xx_driver[] = {
        .phy_id         = PHY_ID_BCM8706,
        .phy_id_mask    = 0xffffffff,
        .name           = "Broadcom BCM8706",
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm87xx_config_init,
        .config_aneg    = bcm87xx_config_aneg,
        .read_status    = bcm87xx_read_status,
@@ -205,7 +208,6 @@ static struct phy_driver bcm87xx_driver[] = {
        .phy_id         = PHY_ID_BCM8727,
        .phy_id_mask    = 0xffffffff,
        .name           = "Broadcom BCM8727",
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm87xx_config_init,
        .config_aneg    = bcm87xx_config_aneg,
        .read_status    = bcm87xx_read_status,
index 7045370..aa73c5c 100644 (file)
@@ -602,7 +602,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5411",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -611,7 +610,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5421",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -620,7 +618,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM54210E",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -629,7 +626,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5461",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -638,7 +634,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM54612E",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -647,7 +642,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM54616S",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .config_aneg    = bcm54616s_config_aneg,
        .ack_interrupt  = bcm_phy_ack_intr,
@@ -657,7 +651,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5464",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -666,7 +659,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5481",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .config_aneg    = bcm5481_config_aneg,
        .ack_interrupt  = bcm_phy_ack_intr,
@@ -676,7 +668,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM54810",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .config_aneg    = bcm5481_config_aneg,
        .ack_interrupt  = bcm_phy_ack_intr,
@@ -686,7 +677,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5482",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm5482_config_init,
        .read_status    = bcm5482_read_status,
        .ack_interrupt  = bcm_phy_ack_intr,
@@ -696,7 +686,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM50610",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -705,7 +694,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM50610M",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -714,7 +702,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM57780",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
@@ -723,7 +710,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCMAC131",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = brcm_fet_config_init,
        .ack_interrupt  = brcm_fet_ack_interrupt,
        .config_intr    = brcm_fet_config_intr,
@@ -732,7 +718,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM5241",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = brcm_fet_config_init,
        .ack_interrupt  = brcm_fet_ack_interrupt,
        .config_intr    = brcm_fet_config_intr,
@@ -751,7 +736,6 @@ static struct phy_driver broadcom_drivers[] = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "Broadcom BCM89610",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = bcm54xx_config_init,
        .ack_interrupt  = bcm_phy_ack_intr,
        .config_intr    = bcm_phy_config_intr,
index c05af00..fea61c8 100644 (file)
@@ -108,7 +108,6 @@ static struct phy_driver cis820x_driver[] = {
        .name           = "Cicada Cis8201",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &cis820x_config_init,
        .ack_interrupt  = &cis820x_ack_interrupt,
        .config_intr    = &cis820x_config_intr,
@@ -117,7 +116,6 @@ static struct phy_driver cis820x_driver[] = {
        .name           = "Cicada Cis8204",
        .phy_id_mask    = 0x000fffc0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &cis820x_config_init,
        .ack_interrupt  = &cis820x_ack_interrupt,
        .config_intr    = &cis820x_config_intr,
index 5ee99b3..9716200 100644 (file)
@@ -150,7 +150,6 @@ static struct phy_driver dm91xx_driver[] = {
        .name           = "Davicom DM9161E",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = dm9161_config_init,
        .config_aneg    = dm9161_config_aneg,
        .ack_interrupt  = dm9161_ack_interrupt,
@@ -160,7 +159,6 @@ static struct phy_driver dm91xx_driver[] = {
        .name           = "Davicom DM9161B/C",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = dm9161_config_init,
        .config_aneg    = dm9161_config_aneg,
        .ack_interrupt  = dm9161_ack_interrupt,
@@ -170,7 +168,6 @@ static struct phy_driver dm91xx_driver[] = {
        .name           = "Davicom DM9161A",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = dm9161_config_init,
        .config_aneg    = dm9161_config_aneg,
        .ack_interrupt  = dm9161_ack_interrupt,
@@ -180,7 +177,6 @@ static struct phy_driver dm91xx_driver[] = {
        .name           = "Davicom DM9131",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .ack_interrupt  = dm9161_ack_interrupt,
        .config_intr    = dm9161_config_intr,
 } };
index edd4d44..18b41bc 100644 (file)
@@ -1521,7 +1521,6 @@ static struct phy_driver dp83640_driver = {
        .phy_id_mask    = 0xfffffff0,
        .name           = "NatSemi DP83640",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .probe          = dp83640_probe,
        .remove         = dp83640_remove,
        .soft_reset     = dp83640_soft_reset,
index 6e8a2a4..24c7f14 100644 (file)
@@ -318,7 +318,6 @@ static struct phy_driver dp83822_driver[] = {
                .phy_id_mask = 0xfffffff0,
                .name = "TI DP83822",
                .features = PHY_BASIC_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .config_init = dp83822_config_init,
                .soft_reset = dp83822_phy_reset,
                .get_wol = dp83822_get_wol,
index 6e8e423..a6b5590 100644 (file)
@@ -108,7 +108,6 @@ MODULE_DEVICE_TABLE(mdio, dp83848_tbl);
                .phy_id_mask    = 0xfffffff0,                   \
                .name           = _name,                        \
                .features       = PHY_BASIC_FEATURES,           \
-               .flags          = PHY_HAS_INTERRUPT,            \
                                                                \
                .soft_reset     = genphy_soft_reset,            \
                .config_init    = _config_init,                 \
index b393577..da6a67d 100644 (file)
@@ -334,7 +334,6 @@ static struct phy_driver dp83867_driver[] = {
                .phy_id_mask    = 0xfffffff0,
                .name           = "TI DP83867",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
 
                .config_init    = dp83867_config_init,
                .soft_reset     = dp83867_phy_reset,
index 78cad13..da13356 100644 (file)
@@ -346,7 +346,6 @@ static struct phy_driver dp83811_driver[] = {
                .phy_id_mask = 0xfffffff0,
                .name = "TI DP83TC811",
                .features = PHY_BASIC_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .config_init = dp83811_config_init,
                .config_aneg = dp83811_config_aneg,
                .soft_reset = dp83811_phy_reset,
index 67b2608..f7fb627 100644 (file)
@@ -223,14 +223,23 @@ struct phy_device *fixed_phy_register(unsigned int irq,
 
        switch (status->speed) {
        case SPEED_1000:
-               phy->supported = PHY_1000BT_FEATURES;
-               break;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                phy->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                phy->supported);
+               /* fall through */
        case SPEED_100:
-               phy->supported = PHY_100BT_FEATURES;
-               break;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                phy->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                phy->supported);
+               /* fall through */
        case SPEED_10:
        default:
-               phy->supported = PHY_10BT_FEATURES;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                                phy->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                                phy->supported);
        }
 
        ret = phy_device_register(phy);
index 791587a..7d5938b 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/phy.h>
+#include <linux/property.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -36,14 +37,34 @@ MODULE_LICENSE("GPL");
 
 /* IP101A/G - IP1001 */
 #define IP10XX_SPEC_CTRL_STATUS                16      /* Spec. Control Register */
-#define IP1001_RXPHASE_SEL             (1<<0)  /* Add delay on RX_CLK */
-#define IP1001_TXPHASE_SEL             (1<<1)  /* Add delay on TX_CLK */
+#define IP1001_RXPHASE_SEL             BIT(0)  /* Add delay on RX_CLK */
+#define IP1001_TXPHASE_SEL             BIT(1)  /* Add delay on TX_CLK */
 #define IP1001_SPEC_CTRL_STATUS_2      20      /* IP1001 Spec. Control Reg 2 */
 #define IP1001_APS_ON                  11      /* IP1001 APS Mode  bit */
-#define IP101A_G_APS_ON                        2       /* IP101A/G APS Mode bit */
+#define IP101A_G_APS_ON                        BIT(1)  /* IP101A/G APS Mode bit */
 #define IP101A_G_IRQ_CONF_STATUS       0x11    /* Conf Info IRQ & Status Reg */
-#define        IP101A_G_IRQ_PIN_USED           (1<<15) /* INTR pin used */
-#define        IP101A_G_IRQ_DEFAULT            IP101A_G_IRQ_PIN_USED
+#define        IP101A_G_IRQ_PIN_USED           BIT(15) /* INTR pin used */
+#define IP101A_G_IRQ_ALL_MASK          BIT(11) /* IRQ's inactive */
+#define IP101A_G_IRQ_SPEED_CHANGE      BIT(2)
+#define IP101A_G_IRQ_DUPLEX_CHANGE     BIT(1)
+#define IP101A_G_IRQ_LINK_CHANGE       BIT(0)
+
+#define IP101G_DIGITAL_IO_SPEC_CTRL                    0x1d
+#define IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32         BIT(2)
+
+/* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin
+ * (pin number 21). The hardware default is RXER (receive error) mode. But it
+ * can be configured to interrupt mode manually.
+ */
+enum ip101gr_sel_intr32 {
+       IP101GR_SEL_INTR32_KEEP,
+       IP101GR_SEL_INTR32_INTR,
+       IP101GR_SEL_INTR32_RXER,
+};
+
+struct ip101a_g_phy_priv {
+       enum ip101gr_sel_intr32 sel_intr32;
+};
 
 static int ip175c_config_init(struct phy_device *phydev)
 {
@@ -162,18 +183,92 @@ static int ip1001_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static int ip175c_read_status(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 4) /* WAN port */
+               genphy_read_status(phydev);
+       else
+               /* Don't need to read status for switch ports */
+               phydev->irq = PHY_IGNORE_INTERRUPT;
+
+       return 0;
+}
+
+static int ip175c_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 4) /* WAN port */
+               genphy_config_aneg(phydev);
+
+       return 0;
+}
+
+static int ip101a_g_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct ip101a_g_phy_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* Both functions (RX error and interrupt status) are sharing the same
+        * pin on the 32-pin IP101GR, so this is an exclusive choice.
+        */
+       if (device_property_read_bool(dev, "icplus,select-rx-error") &&
+           device_property_read_bool(dev, "icplus,select-interrupt")) {
+               dev_err(dev,
+                       "RXER and INTR mode cannot be selected together\n");
+               return -EINVAL;
+       }
+
+       if (device_property_read_bool(dev, "icplus,select-rx-error"))
+               priv->sel_intr32 = IP101GR_SEL_INTR32_RXER;
+       else if (device_property_read_bool(dev, "icplus,select-interrupt"))
+               priv->sel_intr32 = IP101GR_SEL_INTR32_INTR;
+       else
+               priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
 static int ip101a_g_config_init(struct phy_device *phydev)
 {
-       int c;
+       struct ip101a_g_phy_priv *priv = phydev->priv;
+       int err, c;
 
        c = ip1xx_reset(phydev);
        if (c < 0)
                return c;
 
-       /* INTR pin used: speed/link/duplex will cause an interrupt */
-       c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT);
-       if (c < 0)
-               return c;
+       /* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */
+       switch (priv->sel_intr32) {
+       case IP101GR_SEL_INTR32_RXER:
+               err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0);
+               if (err < 0)
+                       return err;
+               break;
+
+       case IP101GR_SEL_INTR32_INTR:
+               err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32,
+                                IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32);
+               if (err < 0)
+                       return err;
+               break;
+
+       default:
+               /* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not
+                * documented on IP101A and it's not clear whether this would
+                * cause problems.
+                * For the 32-pin IP101GR we simply keep the SEL_INTR32
+                * configuration as set by the bootloader when not configured
+                * to one of the special functions.
+                */
+               break;
+       }
 
        /* Enable Auto Power Saving mode */
        c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
@@ -182,23 +277,29 @@ static int ip101a_g_config_init(struct phy_device *phydev)
        return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
 }
 
-static int ip175c_read_status(struct phy_device *phydev)
+static int ip101a_g_config_intr(struct phy_device *phydev)
 {
-       if (phydev->mdio.addr == 4) /* WAN port */
-               genphy_read_status(phydev);
+       u16 val;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               /* INTR pin used: Speed/link/duplex will cause an interrupt */
+               val = IP101A_G_IRQ_PIN_USED;
        else
-               /* Don't need to read status for switch ports */
-               phydev->irq = PHY_IGNORE_INTERRUPT;
+               val = IP101A_G_IRQ_ALL_MASK;
 
-       return 0;
+       return phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val);
 }
 
-static int ip175c_config_aneg(struct phy_device *phydev)
+static int ip101a_g_did_interrupt(struct phy_device *phydev)
 {
-       if (phydev->mdio.addr == 4) /* WAN port */
-               genphy_config_aneg(phydev);
+       int val = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS);
 
-       return 0;
+       if (val < 0)
+               return 0;
+
+       return val & (IP101A_G_IRQ_SPEED_CHANGE |
+                     IP101A_G_IRQ_DUPLEX_CHANGE |
+                     IP101A_G_IRQ_LINK_CHANGE);
 }
 
 static int ip101a_g_ack_interrupt(struct phy_device *phydev)
@@ -234,7 +335,9 @@ static struct phy_driver icplus_driver[] = {
        .name           = "ICPlus IP101A/G",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
+       .probe          = ip101a_g_probe,
+       .config_intr    = ip101a_g_config_intr,
+       .did_interrupt  = ip101a_g_did_interrupt,
        .ack_interrupt  = ip101a_g_ack_interrupt,
        .config_init    = &ip101a_g_config_init,
        .suspend        = genphy_suspend,
index 7d936fb..fc0f502 100644 (file)
@@ -242,7 +242,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .config_aneg    = xway_gphy14_config_aneg,
                .ack_interrupt  = xway_gphy_ack_interrupt,
@@ -255,7 +254,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY22F (PEF 7061) v1.3",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .config_aneg    = xway_gphy14_config_aneg,
                .ack_interrupt  = xway_gphy_ack_interrupt,
@@ -268,7 +266,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .config_aneg    = xway_gphy14_config_aneg,
                .ack_interrupt  = xway_gphy_ack_interrupt,
@@ -281,7 +278,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY22F (PEF 7061) v1.4",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .config_aneg    = xway_gphy14_config_aneg,
                .ack_interrupt  = xway_gphy_ack_interrupt,
@@ -294,7 +290,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
@@ -306,7 +301,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
@@ -318,7 +312,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY11G (xRX v1.1 integrated)",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
@@ -330,7 +323,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY22F (xRX v1.1 integrated)",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
@@ -342,7 +334,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY11G (xRX v1.2 integrated)",
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
@@ -354,7 +345,6 @@ static struct phy_driver xway_gphy[] = {
                .phy_id_mask    = 0xffffffff,
                .name           = "Intel XWAY PHY22F (xRX v1.2 integrated)",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = xway_gphy_config_init,
                .ack_interrupt  = xway_gphy_ack_interrupt,
                .did_interrupt  = xway_gphy_did_interrupt,
index c14b254..c8bb29a 100644 (file)
@@ -177,7 +177,7 @@ static int lxt973a2_read_status(struct phy_device *phydev)
                        */
                } while (lpa == adv && retry--);
 
-               phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(lpa);
+               mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
 
                lpa &= adv;
 
@@ -218,7 +218,7 @@ static int lxt973a2_read_status(struct phy_device *phydev)
                        phydev->speed = SPEED_10;
 
                phydev->pause = phydev->asym_pause = 0;
-               phydev->lp_advertising = 0;
+               linkmode_zero(phydev->lp_advertising);
        }
 
        return 0;
@@ -257,7 +257,6 @@ static struct phy_driver lxt97x_driver[] = {
        .name           = "LXT970",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = lxt970_config_init,
        .ack_interrupt  = lxt970_ack_interrupt,
        .config_intr    = lxt970_config_intr,
@@ -266,7 +265,6 @@ static struct phy_driver lxt97x_driver[] = {
        .name           = "LXT971",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .ack_interrupt  = lxt971_ack_interrupt,
        .config_intr    = lxt971_config_intr,
 }, {
index cbec296..a9c7c7f 100644 (file)
@@ -491,25 +491,26 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
 }
 
 /**
- * ethtool_adv_to_fiber_adv_t
- * @ethadv: the ethtool advertisement settings
+ * linkmode_adv_to_fiber_adv_t
+ * @advertise: the linkmode advertisement settings
  *
- * A small helper function that translates ethtool advertisement
- * settings to phy autonegotiation advertisements for the
- * MII_ADV register for fiber link.
+ * A small helper function that translates linkmode advertisement
+ * settings to phy autonegotiation advertisements for the MII_ADV
+ * register for fiber link.
  */
-static inline u32 ethtool_adv_to_fiber_adv_t(u32 ethadv)
+static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise)
 {
        u32 result = 0;
 
-       if (ethadv & ADVERTISED_1000baseT_Half)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise))
                result |= ADVERTISE_FIBER_1000HALF;
-       if (ethadv & ADVERTISED_1000baseT_Full)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise))
                result |= ADVERTISE_FIBER_1000FULL;
 
-       if ((ethadv & ADVERTISE_PAUSE_ASYM) && (ethadv & ADVERTISE_PAUSE_CAP))
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) &&
+           linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
                result |= LPA_PAUSE_ASYM_FIBER;
-       else if (ethadv & ADVERTISE_PAUSE_CAP)
+       else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise))
                result |= (ADVERTISE_PAUSE_FIBER
                           & (~ADVERTISE_PAUSE_ASYM_FIBER));
 
@@ -530,14 +531,13 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev)
        int changed = 0;
        int err;
        int adv, oldadv;
-       u32 advertise;
 
        if (phydev->autoneg != AUTONEG_ENABLE)
                return genphy_setup_forced(phydev);
 
        /* Only allow advertising what this PHY supports */
-       phydev->advertising &= phydev->supported;
-       advertise = phydev->advertising;
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
 
        /* Setup fiber advertisement */
        adv = phy_read(phydev, MII_ADVERTISE);
@@ -547,7 +547,7 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev)
        oldadv = adv;
        adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL
                | LPA_PAUSE_FIBER);
-       adv |= ethtool_adv_to_fiber_adv_t(advertise);
+       adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising);
 
        if (adv != oldadv) {
                err = phy_write(phydev, MII_ADVERTISE, adv);
@@ -847,7 +847,6 @@ static int m88e1510_config_init(struct phy_device *phydev)
 
        /* SGMII-to-Copper mode initialization */
        if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
-               u32 pause;
 
                /* Select page 18 */
                err = marvell_set_page(phydev, 18);
@@ -878,9 +877,14 @@ static int m88e1510_config_init(struct phy_device *phydev)
                 * This means we can never be truely sure what was advertised,
                 * so disable Pause support.
                 */
-               pause = SUPPORTED_Pause | SUPPORTED_Asym_Pause;
-               phydev->supported &= ~pause;
-               phydev->advertising &= ~pause;
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                  phydev->supported);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                  phydev->supported);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                  phydev->advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                  phydev->advertising);
        }
 
        return m88e1318_config_init(phydev);
@@ -1043,22 +1047,21 @@ static int m88e1145_config_init(struct phy_device *phydev)
 }
 
 /**
- * fiber_lpa_to_ethtool_lpa_t
+ * fiber_lpa_mod_linkmode_lpa_t
+ * @advertising: the linkmode advertisement settings
  * @lpa: value of the MII_LPA register for fiber link
  *
- * A small helper function that translates MII_LPA
- * bits to ethtool LP advertisement settings.
+ * A small helper function that translates MII_LPA bits to linkmode LP
+ * advertisement settings. Other bits in advertising are left
+ * unchanged.
  */
-static u32 fiber_lpa_to_ethtool_lpa_t(u32 lpa)
+static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
 {
-       u32 result = 0;
-
-       if (lpa & LPA_FIBER_1000HALF)
-               result |= ADVERTISED_1000baseT_Half;
-       if (lpa & LPA_FIBER_1000FULL)
-               result |= ADVERTISED_1000baseT_Full;
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                        advertising, lpa & LPA_FIBER_1000HALF);
 
-       return result;
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                        advertising, lpa & LPA_FIBER_1000FULL);
 }
 
 /**
@@ -1134,9 +1137,8 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
        }
 
        if (!fiber) {
-               phydev->lp_advertising =
-                       mii_stat1000_to_ethtool_lpa_t(lpagb) |
-                       mii_lpa_to_ethtool_lpa_t(lpa);
+               mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa);
+               mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb);
 
                if (phydev->duplex == DUPLEX_FULL) {
                        phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
@@ -1144,7 +1146,7 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
                }
        } else {
                /* The fiber link is only 1000M capable */
-               phydev->lp_advertising = fiber_lpa_to_ethtool_lpa_t(lpa);
+               fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
 
                if (phydev->duplex == DUPLEX_FULL) {
                        if (!(lpa & LPA_PAUSE_FIBER)) {
@@ -1183,7 +1185,7 @@ static int marvell_read_status_page_fixed(struct phy_device *phydev)
 
        phydev->pause = 0;
        phydev->asym_pause = 0;
-       phydev->lp_advertising = 0;
+       linkmode_zero(phydev->lp_advertising);
 
        return 0;
 }
@@ -1235,7 +1237,8 @@ static int marvell_read_status(struct phy_device *phydev)
        int err;
 
        /* Check the fiber mode first */
-       if (phydev->supported & SUPPORTED_FIBRE &&
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                             phydev->supported) &&
            phydev->interface != PHY_INTERFACE_MODE_SGMII) {
                err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
@@ -1278,7 +1281,8 @@ static int marvell_suspend(struct phy_device *phydev)
        int err;
 
        /* Suspend the fiber mode first */
-       if (!(phydev->supported & SUPPORTED_FIBRE)) {
+       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                              phydev->supported)) {
                err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
@@ -1312,7 +1316,8 @@ static int marvell_resume(struct phy_device *phydev)
        int err;
 
        /* Resume the fiber mode first */
-       if (!(phydev->supported & SUPPORTED_FIBRE)) {
+       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                              phydev->supported)) {
                err = marvell_set_page(phydev, MII_MARVELL_FIBER_PAGE);
                if (err < 0)
                        goto error;
@@ -1463,7 +1468,8 @@ error:
 
 static int marvell_get_sset_count(struct phy_device *phydev)
 {
-       if (phydev->supported & SUPPORTED_FIBRE)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                             phydev->supported))
                return ARRAY_SIZE(marvell_hw_stats);
        else
                return ARRAY_SIZE(marvell_hw_stats) - NB_FIBER_STATS;
@@ -2005,7 +2011,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1101",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &marvell_config_init,
                .config_aneg = &m88e1101_config_aneg,
@@ -2024,7 +2029,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1112",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
@@ -2043,7 +2047,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1111",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
@@ -2063,7 +2066,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1118",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1118_config_init,
                .config_aneg = &m88e1118_config_aneg,
@@ -2082,7 +2084,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1121R",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = &m88e1121_probe,
                .config_init = &marvell_config_init,
                .config_aneg = &m88e1121_config_aneg,
@@ -2103,7 +2104,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1318S",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1318_config_init,
                .config_aneg = &m88e1318_config_aneg,
@@ -2126,7 +2126,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1145",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1145_config_init,
                .config_aneg = &m88e1101_config_aneg,
@@ -2146,7 +2145,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1149R",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1149_config_init,
                .config_aneg = &m88e1118_config_aneg,
@@ -2165,7 +2163,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1240",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
                .config_aneg = &marvell_config_aneg,
@@ -2184,7 +2181,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1116R",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1116r_config_init,
                .ack_interrupt = &marvell_ack_interrupt,
@@ -2202,7 +2198,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1510",
                .features = PHY_GBIT_FIBRE_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = &m88e1510_probe,
                .config_init = &m88e1510_config_init,
                .config_aneg = &m88e1510_config_aneg,
@@ -2226,7 +2221,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E1540",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = m88e1510_probe,
                .config_init = &marvell_config_init,
                .config_aneg = &m88e1510_config_aneg,
@@ -2248,7 +2242,6 @@ static struct phy_driver marvell_drivers[] = {
                .name = "Marvell 88E1545",
                .probe = m88e1510_probe,
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .config_init = &marvell_config_init,
                .config_aneg = &m88e1510_config_aneg,
                .read_status = &marvell_read_status,
@@ -2268,7 +2261,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E3016",
                .features = PHY_BASIC_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e3016_config_init,
                .aneg_done = &marvell_aneg_done,
@@ -2289,7 +2281,6 @@ static struct phy_driver marvell_drivers[] = {
                .phy_id_mask = MARVELL_PHY_ID_MASK,
                .name = "Marvell 88E6390",
                .features = PHY_GBIT_FEATURES,
-               .flags = PHY_HAS_INTERRUPT,
                .probe = m88e6390_probe,
                .config_init = &marvell_config_init,
                .config_aneg = &m88e1510_config_aneg,
index 1c9d039..82ab6ed 100644 (file)
@@ -252,7 +252,6 @@ static int mv3310_resume(struct phy_device *phydev)
 static int mv3310_config_init(struct phy_device *phydev)
 {
        __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, };
-       u32 mask;
        int val;
 
        /* Check that the PHY interface type is compatible */
@@ -336,13 +335,9 @@ static int mv3310_config_init(struct phy_device *phydev)
                }
        }
 
-       if (!ethtool_convert_link_mode_to_legacy_u32(&mask, supported))
-               phydev_warn(phydev,
-                           "PHY supports (%*pb) more modes than phylib supports, some modes not supported.\n",
-                           __ETHTOOL_LINK_MODE_MASK_NBITS, supported);
-
-       phydev->supported &= mask;
-       phydev->advertising &= phydev->supported;
+       linkmode_copy(phydev->supported, supported);
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
 
        return 0;
 }
@@ -350,7 +345,7 @@ static int mv3310_config_init(struct phy_device *phydev)
 static int mv3310_config_aneg(struct phy_device *phydev)
 {
        bool changed = false;
-       u32 advertising;
+       u16 reg;
        int ret;
 
        /* We don't support manual MDI control */
@@ -364,31 +359,35 @@ static int mv3310_config_aneg(struct phy_device *phydev)
                return genphy_c45_an_disable_aneg(phydev);
        }
 
-       phydev->advertising &= phydev->supported;
-       advertising = phydev->advertising;
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
 
        ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
                            ADVERTISE_ALL | ADVERTISE_100BASE4 |
                            ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
-                           ethtool_adv_to_mii_adv_t(advertising));
+                           linkmode_adv_to_mii_adv_t(phydev->advertising));
        if (ret < 0)
                return ret;
        if (ret > 0)
                changed = true;
 
+       reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
        ret = mv3310_modify(phydev, MDIO_MMD_AN, MV_AN_CTRL1000,
-                           ADVERTISE_1000FULL | ADVERTISE_1000HALF,
-                           ethtool_adv_to_mii_ctrl1000_t(advertising));
+                           ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg);
        if (ret < 0)
                return ret;
        if (ret > 0)
                changed = true;
 
        /* 10G control register */
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                             phydev->advertising))
+               reg = MDIO_AN_10GBT_CTRL_ADV10G;
+       else
+               reg = 0;
+
        ret = mv3310_modify(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
-                           MDIO_AN_10GBT_CTRL_ADV10G,
-                           advertising & ADVERTISED_10000baseT_Full ?
-                               MDIO_AN_10GBT_CTRL_ADV10G : 0);
+                           MDIO_AN_10GBT_CTRL_ADV10G, reg);
        if (ret < 0)
                return ret;
        if (ret > 0)
@@ -458,7 +457,7 @@ static int mv3310_read_status(struct phy_device *phydev)
 
        phydev->speed = SPEED_UNKNOWN;
        phydev->duplex = DUPLEX_UNKNOWN;
-       phydev->lp_advertising = 0;
+       linkmode_zero(phydev->lp_advertising);
        phydev->link = 0;
        phydev->pause = 0;
        phydev->asym_pause = 0;
@@ -491,7 +490,7 @@ static int mv3310_read_status(struct phy_device *phydev)
                if (val < 0)
                        return val;
 
-               phydev->lp_advertising |= mii_stat1000_to_ethtool_lpa_t(val);
+               mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
 
                if (phydev->autoneg == AUTONEG_ENABLE)
                        phy_resolve_aneg_linkmode(phydev);
index 0fbcedc..ea9a0e3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/mdio-gpio.h>
 #include <linux/mdio-bitbang.h>
 #include <linux/mdio-gpio.h>
 #include <linux/gpio/consumer.h>
@@ -112,6 +113,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
                                          struct mdio_gpio_info *bitbang,
                                          int bus_id)
 {
+       struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);
        struct mii_bus *new_bus;
 
        bitbang->ctrl.ops = &mdio_gpio_ops;
@@ -128,6 +130,11 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
        else
                strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
 
+       if (pdata) {
+               new_bus->phy_mask = pdata->phy_mask;
+               new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
+       }
+
        dev_set_drvdata(dev, new_bus);
 
        return new_bus;
index ddc2c5e..b03bcf2 100644 (file)
@@ -232,7 +232,7 @@ static struct phy_driver meson_gxl_phy[] = {
                .phy_id_mask    = 0xfffffff0,
                .name           = "Meson GXL Internal PHY",
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_IS_INTERNAL | PHY_HAS_INTERRUPT,
+               .flags          = PHY_IS_INTERNAL,
                .config_init    = meson_gxl_config_init,
                .aneg_done      = genphy_aneg_done,
                .read_status    = meson_gxl_read_status,
index 9265dea..c333847 100644 (file)
@@ -311,17 +311,22 @@ static int kszphy_config_init(struct phy_device *phydev)
 
 static int ksz8041_config_init(struct phy_device *phydev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
        struct device_node *of_node = phydev->mdio.dev.of_node;
 
        /* Limit supported and advertised modes in fiber mode */
        if (of_property_read_bool(of_node, "micrel,fiber-mode")) {
                phydev->dev_flags |= MICREL_PHY_FXEN;
-               phydev->supported &= SUPPORTED_100baseT_Full |
-                                    SUPPORTED_100baseT_Half;
-               phydev->supported |= SUPPORTED_FIBRE;
-               phydev->advertising &= ADVERTISED_100baseT_Full |
-                                      ADVERTISED_100baseT_Half;
-               phydev->advertising |= ADVERTISED_FIBRE;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
+
+               linkmode_and(phydev->supported, phydev->supported, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                                phydev->supported);
+               linkmode_and(phydev->advertising, phydev->advertising, mask);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+                                phydev->advertising);
                phydev->autoneg = AUTONEG_DISABLE;
        }
 
@@ -918,7 +923,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KS8737",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ks8737_type,
        .config_init    = kszphy_config_init,
        .ack_interrupt  = kszphy_ack_interrupt,
@@ -930,7 +934,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = 0x00ffffff,
        .name           = "Micrel KSZ8021 or KSZ8031",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8021_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -946,7 +949,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = 0x00ffffff,
        .name           = "Micrel KSZ8031",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8021_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -962,7 +964,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ8041",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8041_type,
        .probe          = kszphy_probe,
        .config_init    = ksz8041_config_init,
@@ -979,7 +980,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ8041RNLI",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8041_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -995,7 +995,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ8051",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8051_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -1011,7 +1010,6 @@ static struct phy_driver ksphy_driver[] = {
        .name           = "Micrel KSZ8001 or KS8721",
        .phy_id_mask    = 0x00fffffc,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8041_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -1027,7 +1025,6 @@ static struct phy_driver ksphy_driver[] = {
        .name           = "Micrel KSZ8081 or KSZ8091",
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz8081_type,
        .probe          = kszphy_probe,
        .config_init    = kszphy_config_init,
@@ -1043,7 +1040,6 @@ static struct phy_driver ksphy_driver[] = {
        .name           = "Micrel KSZ8061",
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = kszphy_config_init,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
@@ -1054,7 +1050,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = 0x000ffffe,
        .name           = "Micrel KSZ9021 Gigabit PHY",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz9021_type,
        .probe          = kszphy_probe,
        .config_init    = ksz9021_config_init,
@@ -1072,7 +1067,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ9031 Gigabit PHY",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz9021_type,
        .probe          = kszphy_probe,
        .config_init    = ksz9031_config_init,
@@ -1089,7 +1083,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Microchip KSZ9131 Gigabit PHY",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .driver_data    = &ksz9021_type,
        .probe          = kszphy_probe,
        .config_init    = ksz9131_config_init,
@@ -1115,7 +1108,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ886X Switch",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = kszphy_config_init,
        .suspend        = genphy_suspend,
        .resume         = genphy_resume,
@@ -1124,7 +1116,6 @@ static struct phy_driver ksphy_driver[] = {
        .phy_id_mask    = MICREL_PHY_ID_MASK,
        .name           = "Micrel KSZ8795",
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = kszphy_config_init,
        .config_aneg    = ksz8873mll_config_aneg,
        .read_status    = ksz8873mll_read_status,
index 04b12e3..7557beb 100644 (file)
@@ -346,7 +346,6 @@ static struct phy_driver microchip_phy_driver[] = {
        .name           = "Microchip LAN88xx",
 
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = lan88xx_probe,
        .remove         = lan88xx_remove,
index c600a85..3d09b47 100644 (file)
@@ -47,7 +47,6 @@ static struct phy_driver microchip_t1_phy_driver[] = {
                .name           = "Microchip LAN87xx T1",
 
                .features       = PHY_BASIC_T1_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
 
                .config_init    = genphy_config_init,
                .config_aneg    = genphy_config_aneg,
index 7cae175..cfe680f 100644 (file)
@@ -1829,7 +1829,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi FE VSC8530",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc85xx_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
@@ -1855,7 +1854,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi VSC8531",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc85xx_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
@@ -1881,7 +1879,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi FE VSC8540 SyncE",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc85xx_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
@@ -1907,7 +1904,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi VSC8541 SyncE",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc85xx_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
@@ -1933,7 +1929,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi GE VSC8574 SyncE",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc8584_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
@@ -1960,7 +1955,6 @@ static struct phy_driver vsc85xx_driver[] = {
        .name           = "Microsemi GE VSC8584 SyncE",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .soft_reset     = &genphy_soft_reset,
        .config_init    = &vsc8584_config_init,
        .config_aneg    = &vsc85xx_config_aneg,
index 2b1e336..139bed2 100644 (file)
@@ -134,7 +134,6 @@ static struct phy_driver dp83865_driver[] = { {
        .phy_id_mask = 0xfffffff0,
        .name = "NatSemi DP83865",
        .features = PHY_GBIT_FEATURES,
-       .flags = PHY_HAS_INTERRUPT,
        .config_init = ns_config_init,
        .ack_interrupt = ns_ack_interrupt,
        .config_intr = ns_config_intr,
index d7636ff..03af927 100644 (file)
@@ -181,7 +181,7 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
        if (val < 0)
                return val;
 
-       phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(val);
+       mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, val);
        phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
        phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
 
@@ -191,7 +191,8 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
                return val;
 
        if (val & MDIO_AN_10GBT_STAT_LP10G)
-               phydev->lp_advertising |= ADVERTISED_10000baseT_Full;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                                phydev->lp_advertising);
 
        return 0;
 }
@@ -304,8 +305,11 @@ EXPORT_SYMBOL_GPL(gen10g_no_soft_reset);
 int gen10g_config_init(struct phy_device *phydev)
 {
        /* Temporarily just say we support everything */
-       phydev->supported = SUPPORTED_10000baseT_Full;
-       phydev->advertising = SUPPORTED_10000baseT_Full;
+       linkmode_zero(phydev->supported);
+
+       linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                        phydev->supported);
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        return 0;
 }
index c7da4cb..20fbd5e 100644 (file)
@@ -62,6 +62,124 @@ EXPORT_SYMBOL_GPL(phy_duplex_to_str);
  * must be grouped by speed and sorted in descending match priority
  * - iow, descending speed. */
 static const struct phy_setting settings[] = {
+       /* 100G */
+       {
+               .speed = SPEED_100000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_100000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_100000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+       },
+       {
+               .speed = SPEED_100000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+       },
+       /* 56G */
+       {
+               .speed = SPEED_56000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_56000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_56000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_56000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
+       },
+       /* 50G */
+       {
+               .speed = SPEED_50000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+       },
+       {
+               .speed = SPEED_50000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+       },
+       {
+               .speed = SPEED_50000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+       },
+       /* 40G */
+       {
+               .speed = SPEED_40000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_40000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_40000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+       },
+       {
+               .speed = SPEED_40000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+       },
+       /* 25G */
+       {
+               .speed = SPEED_25000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+       },
+       {
+               .speed = SPEED_25000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+       },
+       {
+               .speed = SPEED_25000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+       },
+
+       /* 20G */
+       {
+               .speed = SPEED_20000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
+       },
+       {
+               .speed = SPEED_20000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
+       },
+       /* 10G */
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+       },
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+       },
        {
                .speed = SPEED_10000,
                .duplex = DUPLEX_FULL,
@@ -75,22 +193,51 @@ static const struct phy_setting settings[] = {
        {
                .speed = SPEED_10000,
                .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+       },
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+       },
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+       },
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+       },
+       {
+               .speed = SPEED_10000,
+               .duplex = DUPLEX_FULL,
                .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
        },
+       /* 5G */
+       {
+               .speed = SPEED_5000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+       },
+
+       /* 2.5G */
        {
                .speed = SPEED_2500,
                .duplex = DUPLEX_FULL,
-               .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+               .bit = ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
        },
        {
-               .speed = SPEED_1000,
+               .speed = SPEED_2500,
                .duplex = DUPLEX_FULL,
-               .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+               .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
        },
+       /* 1G */
        {
                .speed = SPEED_1000,
                .duplex = DUPLEX_FULL,
-               .bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+               .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
        },
        {
                .speed = SPEED_1000,
@@ -103,6 +250,12 @@ static const struct phy_setting settings[] = {
                .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
        },
        {
+               .speed = SPEED_1000,
+               .duplex = DUPLEX_FULL,
+               .bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+       },
+       /* 100M */
+       {
                .speed = SPEED_100,
                .duplex = DUPLEX_FULL,
                .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
@@ -112,6 +265,7 @@ static const struct phy_setting settings[] = {
                .duplex = DUPLEX_HALF,
                .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
        },
+       /* 10M */
        {
                .speed = SPEED_10,
                .duplex = DUPLEX_FULL,
@@ -129,7 +283,6 @@ static const struct phy_setting settings[] = {
  * @speed: speed to match
  * @duplex: duplex to match
  * @mask: allowed link modes
- * @maxbit: bit size of link modes
  * @exact: an exact match is required
  *
  * Search the settings array for a setting that matches the speed and
@@ -143,14 +296,14 @@ static const struct phy_setting settings[] = {
  * they all fail, %NULL will be returned.
  */
 const struct phy_setting *
-phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
-                  size_t maxbit, bool exact)
+phy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact)
 {
        const struct phy_setting *p, *match = NULL, *last = NULL;
        int i;
 
        for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
-               if (p->bit < maxbit && test_bit(p->bit, mask)) {
+               if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
+                   test_bit(p->bit, mask)) {
                        last = p;
                        if (p->speed == speed && p->duplex == duplex) {
                                /* Exact match for speed and duplex */
@@ -175,13 +328,13 @@ phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
 EXPORT_SYMBOL_GPL(phy_lookup_setting);
 
 size_t phy_speeds(unsigned int *speeds, size_t size,
-                 unsigned long *mask, size_t maxbit)
+                 unsigned long *mask)
 {
        size_t count;
        int i;
 
        for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
-               if (settings[i].bit < maxbit &&
+               if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS &&
                    test_bit(settings[i].bit, mask) &&
                    (count == 0 || speeds[count - 1] != settings[i].speed))
                        speeds[count++] = settings[i].speed;
@@ -199,35 +352,53 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
  */
 void phy_resolve_aneg_linkmode(struct phy_device *phydev)
 {
-       u32 common = phydev->lp_advertising & phydev->advertising;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
 
-       if (common & ADVERTISED_10000baseT_Full) {
+       linkmode_and(common, phydev->lp_advertising, phydev->advertising);
+
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common)) {
                phydev->speed = SPEED_10000;
                phydev->duplex = DUPLEX_FULL;
-       } else if (common & ADVERTISED_1000baseT_Full) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+                                    common)) {
+               phydev->speed = SPEED_5000;
+               phydev->duplex = DUPLEX_FULL;
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                                    common)) {
+               phydev->speed = SPEED_2500;
+               phydev->duplex = DUPLEX_FULL;
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                    common)) {
                phydev->speed = SPEED_1000;
                phydev->duplex = DUPLEX_FULL;
-       } else if (common & ADVERTISED_1000baseT_Half) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                    common)) {
                phydev->speed = SPEED_1000;
                phydev->duplex = DUPLEX_HALF;
-       } else if (common & ADVERTISED_100baseT_Full) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                    common)) {
                phydev->speed = SPEED_100;
                phydev->duplex = DUPLEX_FULL;
-       } else if (common & ADVERTISED_100baseT_Half) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                    common)) {
                phydev->speed = SPEED_100;
                phydev->duplex = DUPLEX_HALF;
-       } else if (common & ADVERTISED_10baseT_Full) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                                    common)) {
                phydev->speed = SPEED_10;
                phydev->duplex = DUPLEX_FULL;
-       } else if (common & ADVERTISED_10baseT_Half) {
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                                    common)) {
                phydev->speed = SPEED_10;
                phydev->duplex = DUPLEX_HALF;
        }
 
        if (phydev->duplex == DUPLEX_FULL) {
-               phydev->pause = !!(phydev->lp_advertising & ADVERTISED_Pause);
-               phydev->asym_pause = !!(phydev->lp_advertising &
-                                       ADVERTISED_Asym_Pause);
+               phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                                 phydev->lp_advertising);
+               phydev->asym_pause = linkmode_test_bit(
+                       ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                       phydev->lp_advertising);
        }
 }
 EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode);
index 1d73ac3..e24708f 100644 (file)
@@ -46,11 +46,8 @@ static const char *phy_state_to_str(enum phy_state st)
 {
        switch (st) {
        PHY_STATE_STR(DOWN)
-       PHY_STATE_STR(STARTING)
        PHY_STATE_STR(READY)
-       PHY_STATE_STR(PENDING)
        PHY_STATE_STR(UP)
-       PHY_STATE_STR(AN)
        PHY_STATE_STR(RUNNING)
        PHY_STATE_STR(NOLINK)
        PHY_STATE_STR(FORCING)
@@ -62,6 +59,17 @@ static const char *phy_state_to_str(enum phy_state st)
        return NULL;
 }
 
+static void phy_link_up(struct phy_device *phydev)
+{
+       phydev->phy_link_change(phydev, true, true);
+       phy_led_trigger_change_speed(phydev);
+}
+
+static void phy_link_down(struct phy_device *phydev, bool do_carrier)
+{
+       phydev->phy_link_change(phydev, false, do_carrier);
+       phy_led_trigger_change_speed(phydev);
+}
 
 /**
  * phy_print_status - Convenience function to print out the current phy status
@@ -105,9 +113,9 @@ static int phy_clear_interrupt(struct phy_device *phydev)
  *
  * Returns 0 on success or < 0 on error.
  */
-static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
+static int phy_config_interrupt(struct phy_device *phydev, bool interrupts)
 {
-       phydev->interrupts = interrupts;
+       phydev->interrupts = interrupts ? 1 : 0;
        if (phydev->drv->config_intr)
                return phydev->drv->config_intr(phydev);
 
@@ -171,11 +179,9 @@ EXPORT_SYMBOL(phy_aneg_done);
  * settings were found.
  */
 static const struct phy_setting *
-phy_find_valid(int speed, int duplex, u32 supported)
+phy_find_valid(int speed, int duplex, unsigned long *supported)
 {
-       unsigned long mask = supported;
-
-       return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false);
+       return phy_lookup_setting(speed, duplex, supported, false);
 }
 
 /**
@@ -192,9 +198,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
                                  unsigned int *speeds,
                                  unsigned int size)
 {
-       unsigned long supported = phy->supported;
-
-       return phy_speeds(speeds, size, &supported, BITS_PER_LONG);
+       return phy_speeds(speeds, size, phy->supported);
 }
 
 /**
@@ -206,11 +210,10 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
  *
  * Description: Returns true if there is a valid setting, false otherwise.
  */
-static inline bool phy_check_valid(int speed, int duplex, u32 features)
+static inline bool phy_check_valid(int speed, int duplex,
+                                  unsigned long *features)
 {
-       unsigned long mask = features;
-
-       return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true);
+       return !!phy_lookup_setting(speed, duplex, features, true);
 }
 
 /**
@@ -224,13 +227,13 @@ static inline bool phy_check_valid(int speed, int duplex, u32 features)
 static void phy_sanitize_settings(struct phy_device *phydev)
 {
        const struct phy_setting *setting;
-       u32 features = phydev->supported;
 
        /* Sanitize settings based on PHY capabilities */
-       if ((features & SUPPORTED_Autoneg) == 0)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported))
                phydev->autoneg = AUTONEG_DISABLE;
 
-       setting = phy_find_valid(phydev->speed, phydev->duplex, features);
+       setting = phy_find_valid(phydev->speed, phydev->duplex,
+                                phydev->supported);
        if (setting) {
                phydev->speed = setting->speed;
                phydev->duplex = setting->duplex;
@@ -256,13 +259,15 @@ static void phy_sanitize_settings(struct phy_device *phydev)
  */
 int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
        u32 speed = ethtool_cmd_speed(cmd);
 
        if (cmd->phy_address != phydev->mdio.addr)
                return -EINVAL;
 
        /* We make sure that we don't pass unsupported values in to the PHY */
-       cmd->advertising &= phydev->supported;
+       ethtool_convert_legacy_u32_to_link_mode(advertising, cmd->advertising);
+       linkmode_and(advertising, advertising, phydev->supported);
 
        /* Verify the settings we care about. */
        if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE)
@@ -283,12 +288,14 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
 
        phydev->speed = speed;
 
-       phydev->advertising = cmd->advertising;
+       linkmode_copy(phydev->advertising, advertising);
 
        if (AUTONEG_ENABLE == cmd->autoneg)
-               phydev->advertising |= ADVERTISED_Autoneg;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                phydev->advertising);
        else
-               phydev->advertising &= ~ADVERTISED_Autoneg;
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                  phydev->advertising);
 
        phydev->duplex = cmd->duplex;
 
@@ -304,25 +311,24 @@ EXPORT_SYMBOL(phy_ethtool_sset);
 int phy_ethtool_ksettings_set(struct phy_device *phydev,
                              const struct ethtool_link_ksettings *cmd)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
        u8 autoneg = cmd->base.autoneg;
        u8 duplex = cmd->base.duplex;
        u32 speed = cmd->base.speed;
-       u32 advertising;
 
        if (cmd->base.phy_address != phydev->mdio.addr)
                return -EINVAL;
 
-       ethtool_convert_link_mode_to_legacy_u32(&advertising,
-                                               cmd->link_modes.advertising);
+       linkmode_copy(advertising, cmd->link_modes.advertising);
 
        /* We make sure that we don't pass unsupported values in to the PHY */
-       advertising &= phydev->supported;
+       linkmode_and(advertising, advertising, phydev->supported);
 
        /* Verify the settings we care about. */
        if (autoneg != AUTONEG_ENABLE && autoneg != AUTONEG_DISABLE)
                return -EINVAL;
 
-       if (autoneg == AUTONEG_ENABLE && advertising == 0)
+       if (autoneg == AUTONEG_ENABLE && linkmode_empty(advertising))
                return -EINVAL;
 
        if (autoneg == AUTONEG_DISABLE &&
@@ -337,12 +343,14 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
 
        phydev->speed = speed;
 
-       phydev->advertising = advertising;
+       linkmode_copy(phydev->advertising, advertising);
 
        if (autoneg == AUTONEG_ENABLE)
-               phydev->advertising |= ADVERTISED_Autoneg;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                phydev->advertising);
        else
-               phydev->advertising &= ~ADVERTISED_Autoneg;
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                  phydev->advertising);
 
        phydev->duplex = duplex;
 
@@ -358,14 +366,9 @@ EXPORT_SYMBOL(phy_ethtool_ksettings_set);
 void phy_ethtool_ksettings_get(struct phy_device *phydev,
                               struct ethtool_link_ksettings *cmd)
 {
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
-                                               phydev->supported);
-
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
-                                               phydev->advertising);
-
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
-                                               phydev->lp_advertising);
+       linkmode_copy(cmd->link_modes.supported, phydev->supported);
+       linkmode_copy(cmd->link_modes.advertising, phydev->advertising);
+       linkmode_copy(cmd->link_modes.lp_advertising, phydev->lp_advertising);
 
        cmd->base.speed = phydev->speed;
        cmd->base.duplex = phydev->duplex;
@@ -434,7 +437,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
                                }
                                break;
                        case MII_ADVERTISE:
-                               phydev->advertising = mii_adv_to_ethtool_adv_t(val);
+                               mii_adv_mod_linkmode_adv_t(phydev->advertising,
+                                                          val);
                                change_autoneg = true;
                                break;
                        default:
@@ -467,6 +471,18 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
 }
 EXPORT_SYMBOL(phy_mii_ioctl);
 
+static void phy_queue_state_machine(struct phy_device *phydev,
+                                   unsigned int secs)
+{
+       mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,
+                        secs * HZ);
+}
+
+static void phy_trigger_machine(struct phy_device *phydev)
+{
+       phy_queue_state_machine(phydev, 0);
+}
+
 static int phy_config_aneg(struct phy_device *phydev)
 {
        if (phydev->drv->config_aneg)
@@ -482,6 +498,34 @@ static int phy_config_aneg(struct phy_device *phydev)
 }
 
 /**
+ * phy_check_link_status - check link status and set state accordingly
+ * @phydev: the phy_device struct
+ *
+ * Description: Check for link and whether autoneg was triggered / is running
+ * and set state accordingly
+ */
+static int phy_check_link_status(struct phy_device *phydev)
+{
+       int err;
+
+       WARN_ON(!mutex_is_locked(&phydev->lock));
+
+       err = phy_read_status(phydev);
+       if (err)
+               return err;
+
+       if (phydev->link && phydev->state != PHY_RUNNING) {
+               phydev->state = PHY_RUNNING;
+               phy_link_up(phydev);
+       } else if (!phydev->link && phydev->state != PHY_NOLINK) {
+               phydev->state = PHY_NOLINK;
+               phy_link_down(phydev, true);
+       }
+
+       return 0;
+}
+
+/**
  * phy_start_aneg - start auto-negotiation for this PHY device
  * @phydev: the phy_device struct
  *
@@ -492,7 +536,6 @@ static int phy_config_aneg(struct phy_device *phydev)
  */
 int phy_start_aneg(struct phy_device *phydev)
 {
-       bool trigger = 0;
        int err;
 
        if (!phydev->drv)
@@ -504,7 +547,7 @@ int phy_start_aneg(struct phy_device *phydev)
                phy_sanitize_settings(phydev);
 
        /* Invalidate LP advertising flags */
-       phydev->lp_advertising = 0;
+       linkmode_zero(phydev->lp_advertising);
 
        err = phy_config_aneg(phydev);
        if (err < 0)
@@ -512,32 +555,16 @@ int phy_start_aneg(struct phy_device *phydev)
 
        if (phydev->state != PHY_HALTED) {
                if (AUTONEG_ENABLE == phydev->autoneg) {
-                       phydev->state = PHY_AN;
-                       phydev->link_timeout = PHY_AN_TIMEOUT;
+                       err = phy_check_link_status(phydev);
                } else {
                        phydev->state = PHY_FORCING;
                        phydev->link_timeout = PHY_FORCE_TIMEOUT;
                }
        }
 
-       /* Re-schedule a PHY state machine to check PHY status because
-        * negotiation may already be done and aneg interrupt may not be
-        * generated.
-        */
-       if (!phy_polling_mode(phydev) && phydev->state == PHY_AN) {
-               err = phy_aneg_done(phydev);
-               if (err > 0) {
-                       trigger = true;
-                       err = 0;
-               }
-       }
-
 out_unlock:
        mutex_unlock(&phydev->lock);
 
-       if (trigger)
-               phy_trigger_machine(phydev);
-
        return err;
 }
 EXPORT_SYMBOL(phy_start_aneg);
@@ -573,20 +600,38 @@ static int phy_poll_aneg_done(struct phy_device *phydev)
  */
 int phy_speed_down(struct phy_device *phydev, bool sync)
 {
-       u32 adv = phydev->lp_advertising & phydev->supported;
-       u32 adv_old = phydev->advertising;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(adv);
        int ret;
 
        if (phydev->autoneg != AUTONEG_ENABLE)
                return 0;
 
-       if (adv & PHY_10BT_FEATURES)
-               phydev->advertising &= ~(PHY_100BT_FEATURES |
-                                        PHY_1000BT_FEATURES);
-       else if (adv & PHY_100BT_FEATURES)
-               phydev->advertising &= ~PHY_1000BT_FEATURES;
+       linkmode_copy(adv_old, phydev->advertising);
+       linkmode_copy(adv, phydev->lp_advertising);
+       linkmode_and(adv, adv, phydev->supported);
+
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, adv) ||
+           linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, adv)) {
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                  phydev->advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                  phydev->advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                  phydev->advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                  phydev->advertising);
+       } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                    adv) ||
+                  linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                    adv)) {
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                  phydev->advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                  phydev->advertising);
+       }
 
-       if (phydev->advertising == adv_old)
+       if (linkmode_equal(phydev->advertising, adv_old))
                return 0;
 
        ret = phy_config_aneg(phydev);
@@ -605,28 +650,36 @@ EXPORT_SYMBOL_GPL(phy_speed_down);
  */
 int phy_speed_up(struct phy_device *phydev)
 {
-       u32 mask = PHY_10BT_FEATURES | PHY_100BT_FEATURES | PHY_1000BT_FEATURES;
-       u32 adv_old = phydev->advertising;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(all_speeds) = { 0, };
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(not_speeds);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(speeds);
+
+       linkmode_copy(adv_old, phydev->advertising);
 
        if (phydev->autoneg != AUTONEG_ENABLE)
                return 0;
 
-       phydev->advertising = (adv_old & ~mask) | (phydev->supported & mask);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, all_speeds);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, all_speeds);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, all_speeds);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, all_speeds);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, all_speeds);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, all_speeds);
 
-       if (phydev->advertising == adv_old)
+       linkmode_andnot(not_speeds, adv_old, all_speeds);
+       linkmode_copy(supported, phydev->supported);
+       linkmode_and(speeds, supported, all_speeds);
+       linkmode_or(phydev->advertising, not_speeds, speeds);
+
+       if (linkmode_equal(phydev->advertising, adv_old))
                return 0;
 
        return phy_config_aneg(phydev);
 }
 EXPORT_SYMBOL_GPL(phy_speed_up);
 
-static void phy_queue_state_machine(struct phy_device *phydev,
-                                   unsigned int secs)
-{
-       mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,
-                        secs * HZ);
-}
-
 /**
  * phy_start_machine - start PHY state machine tracking
  * @phydev: the phy_device struct
@@ -644,20 +697,6 @@ void phy_start_machine(struct phy_device *phydev)
 EXPORT_SYMBOL_GPL(phy_start_machine);
 
 /**
- * phy_trigger_machine - trigger the state machine to run
- *
- * @phydev: the phy_device struct
- *
- * Description: There has been a change in state which requires that the
- *   state machine runs.
- */
-
-void phy_trigger_machine(struct phy_device *phydev)
-{
-       phy_queue_state_machine(phydev, 0);
-}
-
-/**
  * phy_stop_machine - stop the PHY state machine tracking
  * @phydev: target phy_device struct
  *
@@ -711,30 +750,26 @@ static int phy_disable_interrupts(struct phy_device *phydev)
 }
 
 /**
- * phy_change - Called by the phy_interrupt to handle PHY changes
- * @phydev: phy_device struct that interrupted
+ * phy_interrupt - PHY interrupt handler
+ * @irq: interrupt line
+ * @phy_dat: phy_device pointer
+ *
+ * Description: Handle PHY interrupt
  */
-static irqreturn_t phy_change(struct phy_device *phydev)
+static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 {
-       if (phy_interrupt_is_valid(phydev)) {
-               if (phydev->drv->did_interrupt &&
-                   !phydev->drv->did_interrupt(phydev))
-                       return IRQ_NONE;
-
-               if (phydev->state == PHY_HALTED)
-                       if (phy_disable_interrupts(phydev))
-                               goto phy_err;
-       }
+       struct phy_device *phydev = phy_dat;
 
-       mutex_lock(&phydev->lock);
-       if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
-               phydev->state = PHY_CHANGELINK;
-       mutex_unlock(&phydev->lock);
+       if (PHY_HALTED == phydev->state)
+               return IRQ_NONE;                /* It can't be ours.  */
+
+       if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
+               return IRQ_NONE;
 
        /* reschedule state queue work to run as soon as possible */
        phy_trigger_machine(phydev);
 
-       if (phy_interrupt_is_valid(phydev) && phy_clear_interrupt(phydev))
+       if (phy_clear_interrupt(phydev))
                goto phy_err;
        return IRQ_HANDLED;
 
@@ -744,36 +779,6 @@ phy_err:
 }
 
 /**
- * phy_change_work - Scheduled by the phy_mac_interrupt to handle PHY changes
- * @work: work_struct that describes the work to be done
- */
-void phy_change_work(struct work_struct *work)
-{
-       struct phy_device *phydev =
-               container_of(work, struct phy_device, phy_queue);
-
-       phy_change(phydev);
-}
-
-/**
- * phy_interrupt - PHY interrupt handler
- * @irq: interrupt line
- * @phy_dat: phy_device pointer
- *
- * Description: When a PHY interrupt occurs, the handler disables
- * interrupts, and uses phy_change to handle the interrupt.
- */
-static irqreturn_t phy_interrupt(int irq, void *phy_dat)
-{
-       struct phy_device *phydev = phy_dat;
-
-       if (PHY_HALTED == phydev->state)
-               return IRQ_NONE;                /* It can't be ours.  */
-
-       return phy_change(phydev);
-}
-
-/**
  * phy_enable_interrupts - Enable the interrupts from the PHY side
  * @phydev: target phy_device struct
  */
@@ -851,7 +856,7 @@ out_unlock:
        phy_state_machine(&phydev->state_queue.work);
 
        /* Cannot call flush_scheduled_work() here as desired because
-        * of rtnl_lock(), but PHY_HALTED shall guarantee phy_change()
+        * of rtnl_lock(), but PHY_HALTED shall guarantee irq handler
         * will not reenable interrupts.
         */
 }
@@ -874,9 +879,6 @@ void phy_start(struct phy_device *phydev)
        mutex_lock(&phydev->lock);
 
        switch (phydev->state) {
-       case PHY_STARTING:
-               phydev->state = PHY_PENDING;
-               break;
        case PHY_READY:
                phydev->state = PHY_UP;
                break;
@@ -902,18 +904,6 @@ void phy_start(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_start);
 
-static void phy_link_up(struct phy_device *phydev)
-{
-       phydev->phy_link_change(phydev, true, true);
-       phy_led_trigger_change_speed(phydev);
-}
-
-static void phy_link_down(struct phy_device *phydev, bool do_carrier)
-{
-       phydev->phy_link_change(phydev, false, do_carrier);
-       phy_led_trigger_change_speed(phydev);
-}
-
 /**
  * phy_state_machine - Handle the state machine
  * @work: work_struct that describes the work to be done
@@ -936,63 +926,17 @@ void phy_state_machine(struct work_struct *work)
 
        switch (phydev->state) {
        case PHY_DOWN:
-       case PHY_STARTING:
        case PHY_READY:
-       case PHY_PENDING:
                break;
        case PHY_UP:
                needs_aneg = true;
 
-               phydev->link_timeout = PHY_AN_TIMEOUT;
-
-               break;
-       case PHY_AN:
-               err = phy_read_status(phydev);
-               if (err < 0)
-                       break;
-
-               /* If the link is down, give up on negotiation for now */
-               if (!phydev->link) {
-                       phydev->state = PHY_NOLINK;
-                       phy_link_down(phydev, true);
-                       break;
-               }
-
-               /* Check if negotiation is done.  Break if there's an error */
-               err = phy_aneg_done(phydev);
-               if (err < 0)
-                       break;
-
-               /* If AN is done, we're running */
-               if (err > 0) {
-                       phydev->state = PHY_RUNNING;
-                       phy_link_up(phydev);
-               } else if (0 == phydev->link_timeout--)
-                       needs_aneg = true;
                break;
        case PHY_NOLINK:
-               if (!phy_polling_mode(phydev))
-                       break;
-
-               err = phy_read_status(phydev);
-               if (err)
-                       break;
-
-               if (phydev->link) {
-                       if (AUTONEG_ENABLE == phydev->autoneg) {
-                               err = phy_aneg_done(phydev);
-                               if (err < 0)
-                                       break;
-
-                               if (!err) {
-                                       phydev->state = PHY_AN;
-                                       phydev->link_timeout = PHY_AN_TIMEOUT;
-                                       break;
-                               }
-                       }
-                       phydev->state = PHY_RUNNING;
-                       phy_link_up(phydev);
-               }
+       case PHY_RUNNING:
+       case PHY_CHANGELINK:
+       case PHY_RESUMING:
+               err = phy_check_link_status(phydev);
                break;
        case PHY_FORCING:
                err = genphy_update_link(phydev);
@@ -1008,32 +952,6 @@ void phy_state_machine(struct work_struct *work)
                        phy_link_down(phydev, false);
                }
                break;
-       case PHY_RUNNING:
-               if (!phy_polling_mode(phydev))
-                       break;
-
-               err = phy_read_status(phydev);
-               if (err)
-                       break;
-
-               if (!phydev->link) {
-                       phydev->state = PHY_NOLINK;
-                       phy_link_down(phydev, true);
-               }
-               break;
-       case PHY_CHANGELINK:
-               err = phy_read_status(phydev);
-               if (err)
-                       break;
-
-               if (phydev->link) {
-                       phydev->state = PHY_RUNNING;
-                       phy_link_up(phydev);
-               } else {
-                       phydev->state = PHY_NOLINK;
-                       phy_link_down(phydev, true);
-               }
-               break;
        case PHY_HALTED:
                if (phydev->link) {
                        phydev->link = 0;
@@ -1041,30 +959,6 @@ void phy_state_machine(struct work_struct *work)
                        do_suspend = true;
                }
                break;
-       case PHY_RESUMING:
-               if (AUTONEG_ENABLE == phydev->autoneg) {
-                       err = phy_aneg_done(phydev);
-                       if (err < 0) {
-                               break;
-                       } else if (!err) {
-                               phydev->state = PHY_AN;
-                               phydev->link_timeout = PHY_AN_TIMEOUT;
-                               break;
-                       }
-               }
-
-               err = phy_read_status(phydev);
-               if (err)
-                       break;
-
-               if (phydev->link) {
-                       phydev->state = PHY_RUNNING;
-                       phy_link_up(phydev);
-               } else  {
-                       phydev->state = PHY_NOLINK;
-                       phy_link_down(phydev, false);
-               }
-               break;
        }
 
        mutex_unlock(&phydev->lock);
@@ -1104,10 +998,34 @@ void phy_state_machine(struct work_struct *work)
 void phy_mac_interrupt(struct phy_device *phydev)
 {
        /* Trigger a state machine change */
-       queue_work(system_power_efficient_wq, &phydev->phy_queue);
+       phy_trigger_machine(phydev);
 }
 EXPORT_SYMBOL(phy_mac_interrupt);
 
+static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv)
+{
+       linkmode_zero(advertising);
+
+       if (eee_adv & MDIO_EEE_100TX)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                advertising);
+       if (eee_adv & MDIO_EEE_1000T)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                advertising);
+       if (eee_adv & MDIO_EEE_10GT)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+                                advertising);
+       if (eee_adv & MDIO_EEE_1000KX)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+                                advertising);
+       if (eee_adv & MDIO_EEE_10GKX4)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+                                advertising);
+       if (eee_adv & MDIO_EEE_10GKR)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+                                advertising);
+}
+
 /**
  * phy_init_eee - init and check the EEE feature
  * @phydev: target phy_device struct
@@ -1126,9 +1044,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
        /* According to 802.3az,the EEE is supported only in full duplex-mode.
         */
        if (phydev->duplex == DUPLEX_FULL) {
+               __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+               __ETHTOOL_DECLARE_LINK_MODE_MASK(lp);
+               __ETHTOOL_DECLARE_LINK_MODE_MASK(adv);
                int eee_lp, eee_cap, eee_adv;
-               u32 lp, cap, adv;
                int status;
+               u32 cap;
 
                /* Read phy status to properly get the right settings */
                status = phy_read_status(phydev);
@@ -1155,9 +1076,11 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
                if (eee_adv <= 0)
                        goto eee_exit_err;
 
-               adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
-               lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
-               if (!phy_check_valid(phydev->speed, phydev->duplex, lp & adv))
+               mmd_eee_adv_to_linkmode(adv, eee_adv);
+               mmd_eee_adv_to_linkmode(lp, eee_lp);
+               linkmode_and(common, adv, lp);
+
+               if (!phy_check_valid(phydev->speed, phydev->duplex, common))
                        goto eee_exit_err;
 
                if (clk_stop_enable) {
@@ -1221,6 +1144,7 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
        if (val < 0)
                return val;
        data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
+       data->eee_enabled = !!data->advertised;
 
        /* Get LP advertisement EEE */
        val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
@@ -1228,6 +1152,8 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
                return val;
        data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
 
+       data->eee_active = !!(data->advertised & data->lp_advertised);
+
        return 0;
 }
 EXPORT_SYMBOL(phy_ethtool_get_eee);
@@ -1241,7 +1167,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
  */
 int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
 {
-       int cap, old_adv, adv, ret;
+       int cap, old_adv, adv = 0, ret;
 
        if (!phydev->drv)
                return -EIO;
@@ -1255,10 +1181,12 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
        if (old_adv < 0)
                return old_adv;
 
-       adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
-
-       /* Mask prohibited EEE modes */
-       adv &= ~phydev->eee_broken_modes;
+       if (data->eee_enabled) {
+               adv = !data->advertised ? cap :
+                     ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
+               /* Mask prohibited EEE modes */
+               adv &= ~phydev->eee_broken_modes;
+       }
 
        if (old_adv != adv) {
                ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
index 18e92c1..7d5d698 100644 (file)
@@ -66,10 +66,12 @@ static const int phy_basic_ports_array[] = {
        ETHTOOL_LINK_MODE_TP_BIT,
        ETHTOOL_LINK_MODE_MII_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_basic_ports_array);
 
 static const int phy_fibre_port_array[] = {
        ETHTOOL_LINK_MODE_FIBRE_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_fibre_port_array);
 
 static const int phy_all_ports_features_array[] = {
        ETHTOOL_LINK_MODE_Autoneg_BIT,
@@ -80,27 +82,32 @@ static const int phy_all_ports_features_array[] = {
        ETHTOOL_LINK_MODE_BNC_BIT,
        ETHTOOL_LINK_MODE_Backplane_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_all_ports_features_array);
 
-static const int phy_10_100_features_array[] = {
+const int phy_10_100_features_array[4] = {
        ETHTOOL_LINK_MODE_10baseT_Half_BIT,
        ETHTOOL_LINK_MODE_10baseT_Full_BIT,
        ETHTOOL_LINK_MODE_100baseT_Half_BIT,
        ETHTOOL_LINK_MODE_100baseT_Full_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_10_100_features_array);
 
-static const int phy_basic_t1_features_array[] = {
+const int phy_basic_t1_features_array[2] = {
        ETHTOOL_LINK_MODE_TP_BIT,
        ETHTOOL_LINK_MODE_100baseT_Full_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_basic_t1_features_array);
 
-static const int phy_gbit_features_array[] = {
+const int phy_gbit_features_array[2] = {
        ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
        ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_gbit_features_array);
 
-static const int phy_10gbit_features_array[] = {
+const int phy_10gbit_features_array[1] = {
        ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
 };
+EXPORT_SYMBOL_GPL(phy_10gbit_features_array);
 
 __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init;
 EXPORT_SYMBOL_GPL(phy_10gbit_full_features);
@@ -587,7 +594,6 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 
        mutex_init(&dev->lock);
        INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
-       INIT_WORK(&dev->phy_queue, phy_change_work);
 
        /* Request the appropriate module unconditionally; don't
         * bother trying to do so only if it isn't already loaded,
@@ -599,7 +605,21 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
         * driver will get bored and give up as soon as it finds that
         * there's no driver _already_ loaded.
         */
-       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
+       if (is_c45 && c45_ids) {
+               const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
+               int i;
+
+               for (i = 1; i < num_ids; i++) {
+                       if (!(c45_ids->devices_in_package & (1 << i)))
+                               continue;
+
+                       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                                      MDIO_ID_ARGS(c45_ids->device_ids[i]));
+               }
+       } else {
+               request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                              MDIO_ID_ARGS(phy_id));
+       }
 
        device_initialize(&mdiodev->dev);
 
@@ -1442,8 +1462,13 @@ static int genphy_config_advert(struct phy_device *phydev)
        int err, changed = 0;
 
        /* Only allow advertising what this PHY supports */
-       phydev->advertising &= phydev->supported;
-       advertise = phydev->advertising;
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
+       if (!ethtool_convert_link_mode_to_legacy_u32(&advertise,
+                                                    phydev->advertising))
+               phydev_warn(phydev, "PHY advertising (%*pb) more modes than genphy supports, some modes not advertised.\n",
+                           __ETHTOOL_LINK_MODE_MASK_NBITS,
+                           phydev->advertising);
 
        /* Setup standard advertisement */
        adv = phy_read(phydev, MII_ADVERTISE);
@@ -1482,10 +1507,11 @@ static int genphy_config_advert(struct phy_device *phydev)
        oldadv = adv;
        adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
 
-       if (phydev->supported & (SUPPORTED_1000baseT_Half |
-                                SUPPORTED_1000baseT_Full)) {
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                             phydev->supported) ||
+           linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                             phydev->supported))
                adv |= ethtool_adv_to_mii_ctrl1000_t(advertise);
-       }
 
        if (adv != oldadv)
                changed = 1;
@@ -1690,11 +1716,13 @@ int genphy_read_status(struct phy_device *phydev)
        if (err)
                return err;
 
-       phydev->lp_advertising = 0;
+       linkmode_zero(phydev->lp_advertising);
 
        if (AUTONEG_ENABLE == phydev->autoneg) {
-               if (phydev->supported & (SUPPORTED_1000baseT_Half
-                                       | SUPPORTED_1000baseT_Full)) {
+               if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                     phydev->supported) ||
+                   linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                     phydev->supported)) {
                        lpagb = phy_read(phydev, MII_STAT1000);
                        if (lpagb < 0)
                                return lpagb;
@@ -1711,8 +1739,8 @@ int genphy_read_status(struct phy_device *phydev)
                                return -ENOLINK;
                        }
 
-                       phydev->lp_advertising =
-                               mii_stat1000_to_ethtool_lpa_t(lpagb);
+                       mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+                                                       lpagb);
                        common_adv_gb = lpagb & adv << 2;
                }
 
@@ -1720,7 +1748,7 @@ int genphy_read_status(struct phy_device *phydev)
                if (lpa < 0)
                        return lpa;
 
-               phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa);
+               mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
 
                adv = phy_read(phydev, MII_ADVERTISE);
                if (adv < 0)
@@ -1801,11 +1829,13 @@ EXPORT_SYMBOL(genphy_soft_reset);
 int genphy_config_init(struct phy_device *phydev)
 {
        int val;
-       u32 features;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(features) = { 0, };
 
-       features = (SUPPORTED_TP | SUPPORTED_MII
-                       | SUPPORTED_AUI | SUPPORTED_FIBRE |
-                       SUPPORTED_BNC | SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+       linkmode_set_bit_array(phy_basic_ports_array,
+                              ARRAY_SIZE(phy_basic_ports_array),
+                              features);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, features);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, features);
 
        /* Do we support autonegotiation? */
        val = phy_read(phydev, MII_BMSR);
@@ -1813,16 +1843,16 @@ int genphy_config_init(struct phy_device *phydev)
                return val;
 
        if (val & BMSR_ANEGCAPABLE)
-               features |= SUPPORTED_Autoneg;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, features);
 
        if (val & BMSR_100FULL)
-               features |= SUPPORTED_100baseT_Full;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, features);
        if (val & BMSR_100HALF)
-               features |= SUPPORTED_100baseT_Half;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, features);
        if (val & BMSR_10FULL)
-               features |= SUPPORTED_10baseT_Full;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, features);
        if (val & BMSR_10HALF)
-               features |= SUPPORTED_10baseT_Half;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, features);
 
        if (val & BMSR_ESTATEN) {
                val = phy_read(phydev, MII_ESTATUS);
@@ -1830,13 +1860,15 @@ int genphy_config_init(struct phy_device *phydev)
                        return val;
 
                if (val & ESTATUS_1000_TFULL)
-                       features |= SUPPORTED_1000baseT_Full;
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                        features);
                if (val & ESTATUS_1000_THALF)
-                       features |= SUPPORTED_1000baseT_Half;
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                        features);
        }
 
-       phydev->supported &= features;
-       phydev->advertising &= features;
+       linkmode_and(phydev->supported, phydev->supported, features);
+       linkmode_and(phydev->advertising, phydev->advertising, features);
 
        return 0;
 }
@@ -1882,10 +1914,16 @@ static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
 {
        switch (max_speed) {
        case SPEED_10:
-               phydev->supported &= ~PHY_100BT_FEATURES;
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                                  phydev->supported);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                                  phydev->supported);
                /* fall through */
        case SPEED_100:
-               phydev->supported &= ~PHY_1000BT_FEATURES;
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                                  phydev->supported);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                                  phydev->supported);
                break;
        case SPEED_1000:
                break;
@@ -1904,7 +1942,7 @@ int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
        if (err)
                return err;
 
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        return 0;
 }
@@ -1921,10 +1959,8 @@ EXPORT_SYMBOL(phy_set_max_speed);
  */
 void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode)
 {
-       WARN_ON(link_mode > 31);
-
-       phydev->supported &= ~BIT(link_mode);
-       phydev->advertising = phydev->supported;
+       linkmode_clear_bit(link_mode, phydev->supported);
+       linkmode_copy(phydev->advertising, phydev->supported);
 }
 EXPORT_SYMBOL(phy_remove_link_mode);
 
@@ -1937,9 +1973,9 @@ EXPORT_SYMBOL(phy_remove_link_mode);
  */
 void phy_support_sym_pause(struct phy_device *phydev)
 {
-       phydev->supported &= ~SUPPORTED_Asym_Pause;
-       phydev->supported |= SUPPORTED_Pause;
-       phydev->advertising = phydev->supported;
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
+       linkmode_copy(phydev->advertising, phydev->supported);
 }
 EXPORT_SYMBOL(phy_support_sym_pause);
 
@@ -1951,8 +1987,9 @@ EXPORT_SYMBOL(phy_support_sym_pause);
  */
 void phy_support_asym_pause(struct phy_device *phydev)
 {
-       phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
-       phydev->advertising = phydev->supported;
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
+       linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
+       linkmode_copy(phydev->advertising, phydev->supported);
 }
 EXPORT_SYMBOL(phy_support_asym_pause);
 
@@ -1970,12 +2007,13 @@ EXPORT_SYMBOL(phy_support_asym_pause);
 void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx,
                       bool autoneg)
 {
-       phydev->supported &= ~SUPPORTED_Pause;
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
 
        if (rx && tx && autoneg)
-               phydev->supported |= SUPPORTED_Pause;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                phydev->supported);
 
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->advertising, phydev->supported);
 }
 EXPORT_SYMBOL(phy_set_sym_pause);
 
@@ -1992,20 +2030,29 @@ EXPORT_SYMBOL(phy_set_sym_pause);
  */
 void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx)
 {
-       u16 oldadv = phydev->advertising;
-       u16 newadv = oldadv &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv);
 
-       if (rx)
-               newadv |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
-       if (tx)
-               newadv ^= SUPPORTED_Asym_Pause;
+       linkmode_copy(oldadv, phydev->advertising);
 
-       if (oldadv != newadv) {
-               phydev->advertising = newadv;
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                          phydev->advertising);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                          phydev->advertising);
 
-               if (phydev->autoneg)
-                       phy_start_aneg(phydev);
+       if (rx) {
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                phydev->advertising);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                phydev->advertising);
        }
+
+       if (tx)
+               linkmode_change_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                   phydev->advertising);
+
+       if (!linkmode_equal(oldadv, phydev->advertising) &&
+           phydev->autoneg)
+               phy_start_aneg(phydev);
 }
 EXPORT_SYMBOL(phy_set_asym_pause);
 
@@ -2021,8 +2068,10 @@ EXPORT_SYMBOL(phy_set_asym_pause);
 bool phy_validate_pause(struct phy_device *phydev,
                        struct ethtool_pauseparam *pp)
 {
-       if (!(phydev->supported & SUPPORTED_Pause) ||
-           (!(phydev->supported & SUPPORTED_Asym_Pause) &&
+       if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                              phydev->supported) ||
+           (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                               phydev->supported) &&
             pp->rx_pause != pp->tx_pause))
                return false;
        return true;
@@ -2071,6 +2120,11 @@ static void of_set_phy_eee_broken(struct phy_device *phydev)
        phydev->eee_broken_modes = broken;
 }
 
+static bool phy_drv_supports_irq(struct phy_driver *phydrv)
+{
+       return phydrv->config_intr && phydrv->ack_interrupt;
+}
+
 /**
  * phy_probe - probe and init a PHY device
  * @dev: device to probe and init
@@ -2092,8 +2146,7 @@ static int phy_probe(struct device *dev)
        /* Disable the interrupt if the PHY doesn't support it
         * but the interrupt is still a valid one
         */
-       if (!(phydrv->flags & PHY_HAS_INTERRUPT) &&
-           phy_interrupt_is_valid(phydev))
+        if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
                phydev->irq = PHY_POLL;
 
        if (phydrv->flags & PHY_IS_INTERNAL)
@@ -2106,9 +2159,9 @@ static int phy_probe(struct device *dev)
         * or both of these values
         */
        ethtool_convert_link_mode_to_legacy_u32(&features, phydrv->features);
-       phydev->supported = features;
+       linkmode_copy(phydev->supported, phydrv->features);
        of_set_phy_supported(phydev);
-       phydev->advertising = phydev->supported;
+       linkmode_copy(phydev->advertising, phydev->supported);
 
        /* Get the EEE modes we want to prohibit. We will ask
         * the PHY stop advertising these mode later on
@@ -2128,14 +2181,22 @@ static int phy_probe(struct device *dev)
         */
        if (test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydrv->features) ||
            test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydrv->features)) {
-               phydev->supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                  phydev->supported);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                  phydev->supported);
                if (test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydrv->features))
-                       phydev->supported |= SUPPORTED_Pause;
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                        phydev->supported);
                if (test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
                             phydrv->features))
-                       phydev->supported |= SUPPORTED_Asym_Pause;
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                        phydev->supported);
        } else {
-               phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                phydev->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                phydev->supported);
        }
 
        /* Set the state to READY by default */
index 491efc1..263385b 100644 (file)
@@ -67,7 +67,7 @@ void phy_led_trigger_change_speed(struct phy_device *phy)
 EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed);
 
 static void phy_led_trigger_format_name(struct phy_device *phy, char *buf,
-                                       size_t size, char *suffix)
+                                       size_t size, const char *suffix)
 {
        snprintf(buf, size, PHY_ID_FMT ":%s",
                 phy->mdio.bus->id, phy->mdio.addr, suffix);
@@ -77,20 +77,9 @@ static int phy_led_trigger_register(struct phy_device *phy,
                                    struct phy_led_trigger *plt,
                                    unsigned int speed)
 {
-       char name_suffix[PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE];
-
        plt->speed = speed;
-
-       if (speed < SPEED_1000)
-               snprintf(name_suffix, sizeof(name_suffix), "%dMbps", speed);
-       else if (speed == SPEED_2500)
-               snprintf(name_suffix, sizeof(name_suffix), "2.5Gbps");
-       else
-               snprintf(name_suffix, sizeof(name_suffix), "%dGbps",
-                        DIV_ROUND_CLOSEST(speed, 1000));
-
        phy_led_trigger_format_name(phy, plt->name, sizeof(plt->name),
-                                   name_suffix);
+                                   phy_speed_to_str(speed));
        plt->trigger.name = plt->name;
 
        return led_trigger_register(&plt->trigger);
index 9b8dd0d..e7becc7 100644 (file)
@@ -191,8 +191,7 @@ static int phylink_parse_fixedlink(struct phylink *pl,
        phylink_validate(pl, pl->supported, &pl->link_config);
 
        s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
-                              pl->supported,
-                              __ETHTOOL_LINK_MODE_MASK_NBITS, true);
+                              pl->supported, true);
        linkmode_zero(pl->supported);
        phylink_set(pl->supported, MII);
        if (s) {
@@ -634,13 +633,11 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
 {
        struct phylink_link_state config;
        __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
-       u32 advertising;
        int ret;
 
        memset(&config, 0, sizeof(config));
-       ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
-       ethtool_convert_legacy_u32_to_link_mode(config.advertising,
-                                               phy->advertising);
+       linkmode_copy(supported, phy->supported);
+       linkmode_copy(config.advertising, phy->advertising);
        config.interface = pl->link_config.interface;
 
        /*
@@ -673,15 +670,14 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
        linkmode_copy(pl->link_config.advertising, config.advertising);
 
        /* Restrict the phy advertisement according to the MAC support. */
-       ethtool_convert_link_mode_to_legacy_u32(&advertising, config.advertising);
-       phy->advertising = advertising;
+       linkmode_copy(phy->advertising, config.advertising);
        mutex_unlock(&pl->state_mutex);
        mutex_unlock(&phy->lock);
 
        netdev_dbg(pl->netdev,
-                  "phy: setting supported %*pb advertising 0x%08x\n",
+                  "phy: setting supported %*pb advertising %*pb\n",
                   __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported,
-                  phy->advertising);
+                  __ETHTOOL_LINK_MODE_MASK_NBITS, phy->advertising);
 
        phy_start_machine(phy);
        if (phy->irq > 0)
@@ -1088,8 +1084,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
                 * duplex.
                 */
                s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
-                                      pl->supported,
-                                      __ETHTOOL_LINK_MODE_MASK_NBITS, false);
+                                      pl->supported, false);
                if (!s)
                        return -EINVAL;
 
index 889a4dc..cfe2313 100644 (file)
@@ -116,7 +116,6 @@ static struct phy_driver qs6612_driver[] = { {
        .name           = "QS6612",
        .phy_id_mask    = 0xfffffff0,
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = qs6612_config_init,
        .ack_interrupt  = qs6612_ack_interrupt,
        .config_intr    = qs6612_config_intr,
index 271e8ad..c6010fb 100644 (file)
@@ -213,17 +213,13 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
 
 static struct phy_driver realtek_drvs[] = {
        {
-               .phy_id         = 0x00008201,
+               PHY_ID_MATCH_EXACT(0x00008201),
                .name           = "RTL8201CP Ethernet",
-               .phy_id_mask    = 0x0000ffff,
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
        }, {
-               .phy_id         = 0x001cc816,
+               PHY_ID_MATCH_EXACT(0x001cc816),
                .name           = "RTL8201F Fast Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_BASIC_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .ack_interrupt  = &rtl8201_ack_interrupt,
                .config_intr    = &rtl8201_config_intr,
                .suspend        = genphy_suspend,
@@ -231,19 +227,16 @@ static struct phy_driver realtek_drvs[] = {
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
        }, {
-               .phy_id         = 0x001cc910,
+               PHY_ID_MATCH_EXACT(0x001cc910),
                .name           = "RTL8211 Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
                .config_aneg    = rtl8211_config_aneg,
                .read_mmd       = &genphy_read_mmd_unsupported,
                .write_mmd      = &genphy_write_mmd_unsupported,
        }, {
-               .phy_id         = 0x001cc912,
+               PHY_ID_MATCH_EXACT(0x001cc912),
                .name           = "RTL8211B Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .ack_interrupt  = &rtl821x_ack_interrupt,
                .config_intr    = &rtl8211b_config_intr,
                .read_mmd       = &genphy_read_mmd_unsupported,
@@ -251,39 +244,32 @@ static struct phy_driver realtek_drvs[] = {
                .suspend        = rtl8211b_suspend,
                .resume         = rtl8211b_resume,
        }, {
-               .phy_id         = 0x001cc913,
+               PHY_ID_MATCH_EXACT(0x001cc913),
                .name           = "RTL8211C Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
                .config_init    = rtl8211c_config_init,
                .read_mmd       = &genphy_read_mmd_unsupported,
                .write_mmd      = &genphy_write_mmd_unsupported,
        }, {
-               .phy_id         = 0x001cc914,
+               PHY_ID_MATCH_EXACT(0x001cc914),
                .name           = "RTL8211DN Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .ack_interrupt  = rtl821x_ack_interrupt,
                .config_intr    = rtl8211e_config_intr,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
        }, {
-               .phy_id         = 0x001cc915,
+               PHY_ID_MATCH_EXACT(0x001cc915),
                .name           = "RTL8211E Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .ack_interrupt  = &rtl821x_ack_interrupt,
                .config_intr    = &rtl8211e_config_intr,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
        }, {
-               .phy_id         = 0x001cc916,
+               PHY_ID_MATCH_EXACT(0x001cc916),
                .name           = "RTL8211F Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = &rtl8211f_config_init,
                .ack_interrupt  = &rtl8211f_ack_interrupt,
                .config_intr    = &rtl8211f_config_intr,
@@ -292,11 +278,9 @@ static struct phy_driver realtek_drvs[] = {
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
        }, {
-               .phy_id         = 0x001cc961,
+               PHY_ID_MATCH_EXACT(0x001cc961),
                .name           = "RTL8366RB Gigabit Ethernet",
-               .phy_id_mask    = 0x001fffff,
                .features       = PHY_GBIT_FEATURES,
-               .flags          = PHY_HAS_INTERRUPT,
                .config_init    = &rtl8366rb_config_init,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
@@ -305,15 +289,8 @@ static struct phy_driver realtek_drvs[] = {
 
 module_phy_driver(realtek_drvs);
 
-static struct mdio_device_id __maybe_unused realtek_tbl[] = {
-       { 0x001cc816, 0x001fffff },
-       { 0x001cc910, 0x001fffff },
-       { 0x001cc912, 0x001fffff },
-       { 0x001cc913, 0x001fffff },
-       { 0x001cc914, 0x001fffff },
-       { 0x001cc915, 0x001fffff },
-       { 0x001cc916, 0x001fffff },
-       { 0x001cc961, 0x001fffff },
+static const struct mdio_device_id __maybe_unused realtek_tbl[] = {
+       { PHY_ID_MATCH_VENDOR(0x001cc800) },
        { }
 };
 
index c328208..f9477ff 100644 (file)
@@ -219,7 +219,6 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN83C185",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = smsc_phy_probe,
 
@@ -239,7 +238,6 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN8187",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = smsc_phy_probe,
 
@@ -264,7 +262,6 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN8700",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = smsc_phy_probe,
 
@@ -290,7 +287,6 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN911x Internal PHY",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = smsc_phy_probe,
 
@@ -309,7 +305,7 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN8710/LAN8720",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT | PHY_RST_AFTER_CLK_EN,
+       .flags          = PHY_RST_AFTER_CLK_EN,
 
        .probe          = smsc_phy_probe,
 
@@ -335,7 +331,6 @@ static struct phy_driver smsc_phy_driver[] = {
        .name           = "SMSC LAN8740",
 
        .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
 
        .probe          = smsc_phy_probe,
 
index 2fe9a87..33d7336 100644 (file)
@@ -87,7 +87,6 @@ static struct phy_driver ste10xp_pdriver[] = {
        .phy_id_mask = 0xfffffff0,
        .name = "STe101p",
        .features = PHY_BASIC_FEATURES,
-       .flags = PHY_HAS_INTERRUPT,
        .config_init = ste10Xp_config_init,
        .ack_interrupt = ste10Xp_ack_interrupt,
        .config_intr = ste10Xp_config_intr,
@@ -98,7 +97,6 @@ static struct phy_driver ste10xp_pdriver[] = {
        .phy_id_mask = 0xffffffff,
        .name = "STe100p",
        .features = PHY_BASIC_FEATURES,
-       .flags = PHY_HAS_INTERRUPT,
        .config_init = ste10Xp_config_init,
        .ack_interrupt = ste10Xp_ack_interrupt,
        .config_intr = ste10Xp_config_intr,
index 55f48ee..1e4fc42 100644 (file)
@@ -47,7 +47,7 @@ static int upd60620_read_status(struct phy_device *phydev)
                return phy_state;
 
        phydev->link = 0;
-       phydev->lp_advertising = 0;
+       linkmode_zero(phydev->lp_advertising);
        phydev->pause = 0;
        phydev->asym_pause = 0;
 
@@ -70,8 +70,8 @@ static int upd60620_read_status(struct phy_device *phydev)
                        if (phy_state < 0)
                                return phy_state;
 
-                       phydev->lp_advertising
-                               = mii_lpa_to_ethtool_lpa_t(phy_state);
+                       mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising,
+                                                 phy_state);
 
                        if (phydev->duplex == DUPLEX_FULL) {
                                if (phy_state & LPA_PAUSE_CAP)
index fbf9ad4..0646af4 100644 (file)
@@ -70,7 +70,6 @@
 #define PHY_ID_VSC8244                 0x000fc6c0
 #define PHY_ID_VSC8514                 0x00070670
 #define PHY_ID_VSC8572                 0x000704d0
-#define PHY_ID_VSC8574                 0x000704a0
 #define PHY_ID_VSC8601                 0x00070420
 #define PHY_ID_VSC7385                 0x00070450
 #define PHY_ID_VSC7388                 0x00070480
@@ -303,7 +302,6 @@ static int vsc82xx_config_intr(struct phy_device *phydev)
                         phydev->drv->phy_id == PHY_ID_VSC8244 ||
                         phydev->drv->phy_id == PHY_ID_VSC8514 ||
                         phydev->drv->phy_id == PHY_ID_VSC8572 ||
-                        phydev->drv->phy_id == PHY_ID_VSC8574 ||
                         phydev->drv->phy_id == PHY_ID_VSC8601) ?
                                MII_VSC8244_IMASK_MASK :
                                MII_VSC8221_IMASK_MASK);
@@ -399,7 +397,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8234",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
@@ -409,7 +406,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8244",
        .phy_id_mask    = 0x000fffc0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
@@ -419,7 +415,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8514",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
@@ -429,17 +424,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8572",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_init    = &vsc824x_config_init,
-       .config_aneg    = &vsc82x4_config_aneg,
-       .ack_interrupt  = &vsc824x_ack_interrupt,
-       .config_intr    = &vsc82xx_config_intr,
-}, {
-       .phy_id         = PHY_ID_VSC8574,
-       .name           = "Vitesse VSC8574",
-       .phy_id_mask    = 0x000ffff0,
-       .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
@@ -449,7 +433,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8601",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc8601_config_init,
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
@@ -494,7 +477,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .name           = "Vitesse VSC8662",
        .phy_id_mask    = 0x000ffff0,
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc824x_config_init,
        .config_aneg    = &vsc82x4_config_aneg,
        .ack_interrupt  = &vsc824x_ack_interrupt,
@@ -505,7 +487,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .phy_id_mask    = 0x000ffff0,
        .name           = "Vitesse VSC8221",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc8221_config_init,
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
@@ -515,7 +496,6 @@ static struct phy_driver vsc82xx_driver[] = {
        .phy_id_mask    = 0x000ffff0,
        .name           = "Vitesse VSC8211",
        .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
        .config_init    = &vsc8221_config_init,
        .ack_interrupt  = &vsc824x_ack_interrupt,
        .config_intr    = &vsc82xx_config_intr,
@@ -528,7 +508,6 @@ static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
        { PHY_ID_VSC8244, 0x000fffc0 },
        { PHY_ID_VSC8514, 0x000ffff0 },
        { PHY_ID_VSC8572, 0x000ffff0 },
-       { PHY_ID_VSC8574, 0x000ffff0 },
        { PHY_ID_VSC7385, 0x000ffff0 },
        { PHY_ID_VSC7388, 0x000ffff0 },
        { PHY_ID_VSC7395, 0x000ffff0 },
index 364f514..93576e0 100644 (file)
@@ -1212,7 +1212,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
                goto err_port_enter;
        }
 
-       err = dev_open(port_dev);
+       err = dev_open(port_dev, extack);
        if (err) {
                netdev_dbg(dev, "Device %s opening failed\n",
                           portname);
index 0050200..ea52824 100644 (file)
@@ -188,6 +188,11 @@ struct tun_file {
        struct xdp_rxq_info xdp_rxq;
 };
 
+struct tun_page {
+       struct page *page;
+       int count;
+};
+
 struct tun_flow_entry {
        struct hlist_node hash_link;
        struct rcu_head rcu;
@@ -196,7 +201,7 @@ struct tun_flow_entry {
        u32 rxhash;
        u32 rps_rxhash;
        int queue_index;
-       unsigned long updated;
+       unsigned long updated ____cacheline_aligned_in_smp;
 };
 
 #define TUN_NUM_FLOW_ENTRIES 1024
@@ -524,18 +529,17 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
        unsigned long delay = tun->ageing_time;
        u16 queue_index = tfile->queue_index;
 
-       if (!rxhash)
-               return;
-       else
-               head = &tun->flows[tun_hashfn(rxhash)];
+       head = &tun->flows[tun_hashfn(rxhash)];
 
        rcu_read_lock();
 
        e = tun_flow_find(head, rxhash);
        if (likely(e)) {
                /* TODO: keep queueing to old queue until it's empty? */
-               e->queue_index = queue_index;
-               e->updated = jiffies;
+               if (e->queue_index != queue_index)
+                       e->queue_index = queue_index;
+               if (e->updated != jiffies)
+                       e->updated = jiffies;
                sock_rps_record_flow_hash(e->rps_rxhash);
        } else {
                spin_lock_bh(&tun->lock);
@@ -1249,6 +1253,21 @@ static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp)
        }
 }
 
+static int tun_net_change_carrier(struct net_device *dev, bool new_carrier)
+{
+       if (new_carrier) {
+               struct tun_struct *tun = netdev_priv(dev);
+
+               if (!tun->numqueues)
+                       return -EPERM;
+
+               netif_carrier_on(dev);
+       } else {
+               netif_carrier_off(dev);
+       }
+       return 0;
+}
+
 static const struct net_device_ops tun_netdev_ops = {
        .ndo_uninit             = tun_net_uninit,
        .ndo_open               = tun_net_open,
@@ -1258,6 +1277,7 @@ static const struct net_device_ops tun_netdev_ops = {
        .ndo_select_queue       = tun_select_queue,
        .ndo_set_rx_headroom    = tun_set_headroom,
        .ndo_get_stats64        = tun_net_get_stats64,
+       .ndo_change_carrier     = tun_net_change_carrier,
 };
 
 static void __tun_xdp_flush_tfile(struct tun_file *tfile)
@@ -1340,6 +1360,7 @@ static const struct net_device_ops tap_netdev_ops = {
        .ndo_get_stats64        = tun_net_get_stats64,
        .ndo_bpf                = tun_xdp,
        .ndo_xdp_xmit           = tun_xdp_xmit,
+       .ndo_change_carrier     = tun_net_change_carrier,
 };
 
 static void tun_flow_init(struct tun_struct *tun)
@@ -1473,23 +1494,22 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
        skb->truesize += skb->data_len;
 
        for (i = 1; i < it->nr_segs; i++) {
-               struct page_frag *pfrag = &current->task_frag;
                size_t fragsz = it->iov[i].iov_len;
+               struct page *page;
+               void *frag;
 
                if (fragsz == 0 || fragsz > PAGE_SIZE) {
                        err = -EINVAL;
                        goto free;
                }
-
-               if (!skb_page_frag_refill(fragsz, pfrag, GFP_KERNEL)) {
+               frag = netdev_alloc_frag(fragsz);
+               if (!frag) {
                        err = -ENOMEM;
                        goto free;
                }
-
-               skb_fill_page_desc(skb, i - 1, pfrag->page,
-                                  pfrag->offset, fragsz);
-               page_ref_inc(pfrag->page);
-               pfrag->offset += fragsz;
+               page = virt_to_head_page(frag);
+               skb_fill_page_desc(skb, i - 1, page,
+                                  frag - page_address(page), fragsz);
        }
 
        return skb;
@@ -2381,9 +2401,16 @@ static void tun_sock_write_space(struct sock *sk)
        kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
 }
 
+static void tun_put_page(struct tun_page *tpage)
+{
+       if (tpage->page)
+               __page_frag_cache_drain(tpage->page, tpage->count);
+}
+
 static int tun_xdp_one(struct tun_struct *tun,
                       struct tun_file *tfile,
-                      struct xdp_buff *xdp, int *flush)
+                      struct xdp_buff *xdp, int *flush,
+                      struct tun_page *tpage)
 {
        unsigned int datasize = xdp->data_end - xdp->data;
        struct tun_xdp_hdr *hdr = xdp->data_hard_start;
@@ -2395,6 +2422,7 @@ static int tun_xdp_one(struct tun_struct *tun,
        int buflen = hdr->buflen;
        int err = 0;
        bool skb_xdp = false;
+       struct page *page;
 
        xdp_prog = rcu_dereference(tun->xdp_prog);
        if (xdp_prog) {
@@ -2421,7 +2449,14 @@ static int tun_xdp_one(struct tun_struct *tun,
                case XDP_PASS:
                        break;
                default:
-                       put_page(virt_to_head_page(xdp->data));
+                       page = virt_to_head_page(xdp->data);
+                       if (tpage->page == page) {
+                               ++tpage->count;
+                       } else {
+                               tun_put_page(tpage);
+                               tpage->page = page;
+                               tpage->count = 1;
+                       }
                        return 0;
                }
        }
@@ -2453,7 +2488,8 @@ build:
                        goto out;
        }
 
-       if (!rcu_dereference(tun->steering_prog))
+       if (!rcu_dereference(tun->steering_prog) && tun->numqueues > 1 &&
+           !tfile->detached)
                rxhash = __skb_get_hash_symmetric(skb);
 
        skb_record_rx_queue(skb, tfile->queue_index);
@@ -2485,15 +2521,18 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
                return -EBADFD;
 
        if (ctl && (ctl->type == TUN_MSG_PTR)) {
+               struct tun_page tpage;
                int n = ctl->num;
                int flush = 0;
 
+               memset(&tpage, 0, sizeof(tpage));
+
                local_bh_disable();
                rcu_read_lock();
 
                for (i = 0; i < n; i++) {
                        xdp = &((struct xdp_buff *)ctl->ptr)[i];
-                       tun_xdp_one(tun, tfile, xdp, &flush);
+                       tun_xdp_one(tun, tfile, xdp, &flush, &tpage);
                }
 
                if (flush)
@@ -2502,6 +2541,8 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
                rcu_read_unlock();
                local_bh_enable();
 
+               tun_put_page(&tpage);
+
                ret = total_len;
                goto out;
        }
@@ -2978,12 +3019,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
        struct net *net = sock_net(&tfile->sk);
        struct tun_struct *tun;
        void __user* argp = (void __user*)arg;
+       unsigned int ifindex, carrier;
        struct ifreq ifr;
        kuid_t owner;
        kgid_t group;
        int sndbuf;
        int vnet_hdr_sz;
-       unsigned int ifindex;
        int le;
        int ret;
        bool do_notify = false;
@@ -3267,6 +3308,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
                ret = tun_set_ebpf(tun, &tun->filter_prog, argp);
                break;
 
+       case TUNSETCARRIER:
+               ret = -EFAULT;
+               if (copy_from_user(&carrier, argp, sizeof(carrier)))
+                       goto unlock;
+
+               ret = tun_net_change_carrier(tun->dev, (bool)carrier);
+               break;
+
        default:
                ret = -EINVAL;
                break;
index 418b090..860352a 100644 (file)
@@ -613,4 +613,15 @@ config USB_NET_CH9200
          To compile this driver as a module, choose M here: the
          module will be called ch9200.
 
+config USB_NET_AQC111
+       tristate "Aquantia AQtion USB to 5/2.5GbE Controllers support"
+       depends on USB_USBNET
+       select CRC32
+       help
+         This option adds support for Aquantia AQtion USB
+         Ethernet adapters based on AQC111U/AQC112 chips.
+
+         This driver should work with at least the following devices:
+         * Aquantia AQtion USB to 5GbE
+
 endif # USB_NET_DRIVERS
index 27307a4..99fd12b 100644 (file)
@@ -40,3 +40,4 @@ obj-$(CONFIG_USB_VL600)               += lg-vl600.o
 obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
 obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o
 obj-$(CONFIG_USB_NET_CH9200)   += ch9200.o
+obj-$(CONFIG_USB_NET_AQC111)   += aqc111.o
diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c
new file mode 100644 (file)
index 0000000..57f1c94
--- /dev/null
@@ -0,0 +1,1459 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Aquantia Corp. Aquantia AQtion USB to 5GbE Controller
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2002-2003 TiVo Inc.
+ * Copyright (C) 2017-2018 ASIX
+ * Copyright (C) 2018 Aquantia Corp.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/if_vlan.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/linkmode.h>
+
+#include "aqc111.h"
+
+#define DRIVER_NAME "aqc111"
+
+static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                               u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = usbnet_read_cmd_nopm(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
+                                  USB_RECIP_DEVICE, value, index, data, size);
+
+       if (unlikely(ret < 0))
+               netdev_warn(dev->net,
+                           "Failed to read(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, ret);
+
+       return ret;
+}
+
+static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                          u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
+                             USB_RECIP_DEVICE, value, index, data, size);
+
+       if (unlikely(ret < 0))
+               netdev_warn(dev->net,
+                           "Failed to read(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, ret);
+
+       return ret;
+}
+
+static int aqc111_read16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                 u16 index, u16 *data)
+{
+       int ret = 0;
+
+       ret = aqc111_read_cmd_nopm(dev, cmd, value, index, sizeof(*data), data);
+       le16_to_cpus(data);
+
+       return ret;
+}
+
+static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                            u16 index, u16 *data)
+{
+       int ret = 0;
+
+       ret = aqc111_read_cmd(dev, cmd, value, index, sizeof(*data), data);
+       le16_to_cpus(data);
+
+       return ret;
+}
+
+static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+                             u16 value, u16 index, u16 size, const void *data)
+{
+       int err = -ENOMEM;
+       void *buf = NULL;
+
+       netdev_dbg(dev->net,
+                  "%s cmd=%#x reqtype=%#x value=%#x index=%#x size=%d\n",
+                  __func__, cmd, reqtype, value, index, size);
+
+       if (data) {
+               buf = kmemdup(data, size, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+       }
+
+       err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                             cmd, reqtype, value, index, buf, size,
+                             (cmd == AQ_PHY_POWER) ? AQ_USB_PHY_SET_TIMEOUT :
+                             AQ_USB_SET_TIMEOUT);
+
+       if (unlikely(err < 0))
+               netdev_warn(dev->net,
+                           "Failed to write(0x%x) reg index 0x%04x: %d\n",
+                           cmd, index, err);
+       kfree(buf);
+
+out:
+       return err;
+}
+
+static int aqc111_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                u16 index, u16 size, void *data)
+{
+       int ret;
+
+       ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_DEVICE, value, index, size, data);
+
+       return ret;
+}
+
+static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                           u16 index, u16 size, void *data)
+{
+       int ret;
+
+       if (usb_autopm_get_interface(dev->intf) < 0)
+               return -ENODEV;
+
+       ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_DEVICE, value, index, size, data);
+
+       usb_autopm_put_interface(dev->intf);
+
+       return ret;
+}
+
+static int aqc111_write16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                  u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                             u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write32_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+                                  u16 index, u32 *data)
+{
+       u32 tmp = *data;
+
+       cpu_to_le32s(&tmp);
+
+       return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value,
+                             u16 index, u32 *data)
+{
+       u32 tmp = *data;
+
+       cpu_to_le32s(&tmp);
+
+       return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+                                 u16 index, u16 size, void *data)
+{
+       return usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+                                     USB_RECIP_DEVICE, value, index, data,
+                                     size);
+}
+
+static int aqc111_write16_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+                                   u16 index, u16 *data)
+{
+       u16 tmp = *data;
+
+       cpu_to_le16s(&tmp);
+
+       return aqc111_write_cmd_async(dev, cmd, value, index,
+                                     sizeof(tmp), &tmp);
+}
+
+static void aqc111_get_drvinfo(struct net_device *net,
+                              struct ethtool_drvinfo *info)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       /* Inherit standard device info */
+       usbnet_get_drvinfo(net, info);
+       strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
+       snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u",
+                aqc111_data->fw_ver.major,
+                aqc111_data->fw_ver.minor,
+                aqc111_data->fw_ver.rev);
+       info->eedump_len = 0x00;
+       info->regdump_len = 0x00;
+}
+
+static void aqc111_get_wol(struct net_device *net,
+                          struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       wolinfo->supported = WAKE_MAGIC;
+       wolinfo->wolopts = 0;
+
+       if (aqc111_data->wol_flags & AQ_WOL_FLAG_MP)
+               wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int aqc111_set_wol(struct net_device *net,
+                         struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       if (wolinfo->wolopts & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       aqc111_data->wol_flags = 0;
+       if (wolinfo->wolopts & WAKE_MAGIC)
+               aqc111_data->wol_flags |= AQ_WOL_FLAG_MP;
+
+       return 0;
+}
+
+static void aqc111_speed_to_link_mode(u32 speed,
+                                     struct ethtool_link_ksettings *elk)
+{
+       switch (speed) {
+       case SPEED_5000:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    5000baseT_Full);
+               break;
+       case SPEED_2500:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    2500baseT_Full);
+               break;
+       case SPEED_1000:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    1000baseT_Full);
+               break;
+       case SPEED_100:
+               ethtool_link_ksettings_add_link_mode(elk, advertising,
+                                                    100baseT_Full);
+               break;
+       }
+}
+
+static int aqc111_get_link_ksettings(struct net_device *net,
+                                    struct ethtool_link_ksettings *elk)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u32 speed = SPEED_UNKNOWN;
+
+       ethtool_link_ksettings_zero_link_mode(elk, supported);
+       ethtool_link_ksettings_add_link_mode(elk, supported,
+                                            100baseT_Full);
+       ethtool_link_ksettings_add_link_mode(elk, supported,
+                                            1000baseT_Full);
+       if (usb_speed == USB_SPEED_SUPER) {
+               ethtool_link_ksettings_add_link_mode(elk, supported,
+                                                    2500baseT_Full);
+               ethtool_link_ksettings_add_link_mode(elk, supported,
+                                                    5000baseT_Full);
+       }
+       ethtool_link_ksettings_add_link_mode(elk, supported, TP);
+       ethtool_link_ksettings_add_link_mode(elk, supported, Autoneg);
+
+       elk->base.port = PORT_TP;
+       elk->base.transceiver = XCVR_INTERNAL;
+
+       elk->base.mdio_support = 0x00; /*Not supported*/
+
+       if (aqc111_data->autoneg)
+               linkmode_copy(elk->link_modes.advertising,
+                             elk->link_modes.supported);
+       else
+               aqc111_speed_to_link_mode(aqc111_data->advertised_speed, elk);
+
+       elk->base.autoneg = aqc111_data->autoneg;
+
+       switch (aqc111_data->link_speed) {
+       case AQ_INT_SPEED_5G:
+               speed = SPEED_5000;
+               break;
+       case AQ_INT_SPEED_2_5G:
+               speed = SPEED_2500;
+               break;
+       case AQ_INT_SPEED_1G:
+               speed = SPEED_1000;
+               break;
+       case AQ_INT_SPEED_100M:
+               speed = SPEED_100;
+               break;
+       }
+       elk->base.duplex = DUPLEX_FULL;
+       elk->base.speed = speed;
+
+       return 0;
+}
+
+static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+
+       aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+       aqc111_data->phy_cfg |= AQ_PAUSE;
+       aqc111_data->phy_cfg |= AQ_ASYM_PAUSE;
+       aqc111_data->phy_cfg |= AQ_DOWNSHIFT;
+       aqc111_data->phy_cfg &= ~AQ_DSH_RETRIES_MASK;
+       aqc111_data->phy_cfg |= (3 << AQ_DSH_RETRIES_SHIFT) &
+                               AQ_DSH_RETRIES_MASK;
+
+       if (autoneg == AUTONEG_ENABLE) {
+               switch (speed) {
+               case SPEED_5000:
+                       aqc111_data->phy_cfg |= AQ_ADV_5G;
+                       /* fall-through */
+               case SPEED_2500:
+                       aqc111_data->phy_cfg |= AQ_ADV_2G5;
+                       /* fall-through */
+               case SPEED_1000:
+                       aqc111_data->phy_cfg |= AQ_ADV_1G;
+                       /* fall-through */
+               case SPEED_100:
+                       aqc111_data->phy_cfg |= AQ_ADV_100M;
+                       /* fall-through */
+               }
+       } else {
+               switch (speed) {
+               case SPEED_5000:
+                       aqc111_data->phy_cfg |= AQ_ADV_5G;
+                       break;
+               case SPEED_2500:
+                       aqc111_data->phy_cfg |= AQ_ADV_2G5;
+                       break;
+               case SPEED_1000:
+                       aqc111_data->phy_cfg |= AQ_ADV_1G;
+                       break;
+               case SPEED_100:
+                       aqc111_data->phy_cfg |= AQ_ADV_100M;
+                       break;
+               }
+       }
+
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
+}
+
+static int aqc111_set_link_ksettings(struct net_device *net,
+                                    const struct ethtool_link_ksettings *elk)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u8 autoneg = elk->base.autoneg;
+       u32 speed = elk->base.speed;
+
+       if (autoneg == AUTONEG_ENABLE) {
+               if (aqc111_data->autoneg != AUTONEG_ENABLE) {
+                       aqc111_data->autoneg = AUTONEG_ENABLE;
+                       aqc111_data->advertised_speed =
+                                       (usb_speed == USB_SPEED_SUPER) ?
+                                        SPEED_5000 : SPEED_1000;
+                       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                                            aqc111_data->advertised_speed);
+               }
+       } else {
+               if (speed != SPEED_100 &&
+                   speed != SPEED_1000 &&
+                   speed != SPEED_2500 &&
+                   speed != SPEED_5000 &&
+                   speed != SPEED_UNKNOWN)
+                       return -EINVAL;
+
+               if (elk->base.duplex != DUPLEX_FULL)
+                       return -EINVAL;
+
+               if (usb_speed != USB_SPEED_SUPER && speed > SPEED_1000)
+                       return -EINVAL;
+
+               aqc111_data->autoneg = AUTONEG_DISABLE;
+               if (speed != SPEED_UNKNOWN)
+                       aqc111_data->advertised_speed = speed;
+
+               aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                                    aqc111_data->advertised_speed);
+       }
+
+       return 0;
+}
+
+static const struct ethtool_ops aqc111_ethtool_ops = {
+       .get_drvinfo = aqc111_get_drvinfo,
+       .get_wol = aqc111_get_wol,
+       .set_wol = aqc111_set_wol,
+       .get_msglevel = usbnet_get_msglevel,
+       .set_msglevel = usbnet_set_msglevel,
+       .get_link = ethtool_op_get_link,
+       .get_link_ksettings = aqc111_get_link_ksettings,
+       .set_link_ksettings = aqc111_set_link_ksettings
+};
+
+static int aqc111_change_mtu(struct net_device *net, int new_mtu)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u16 reg16 = 0;
+       u8 buf[5];
+
+       net->mtu = new_mtu;
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                         2, &reg16);
+       if (net->mtu > 1500)
+               reg16 |= SFR_MEDIUM_JUMBO_EN;
+       else
+               reg16 &= ~SFR_MEDIUM_JUMBO_EN;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                          2, &reg16);
+
+       if (dev->net->mtu > 12500 && dev->net->mtu <= 16334) {
+               memcpy(buf, &AQC111_BULKIN_SIZE[2], 5);
+               /* RX bulk configuration */
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+                                5, 5, buf);
+       }
+
+       /* Set high low water level */
+       if (dev->net->mtu <= 4500)
+               reg16 = 0x0810;
+       else if (dev->net->mtu <= 9500)
+               reg16 = 0x1020;
+       else if (dev->net->mtu <= 12500)
+               reg16 = 0x1420;
+       else if (dev->net->mtu <= 16334)
+               reg16 = 0x1A20;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+                          2, &reg16);
+
+       return 0;
+}
+
+static int aqc111_set_mac_addr(struct net_device *net, void *p)
+{
+       struct usbnet *dev = netdev_priv(net);
+       int ret = 0;
+
+       ret = eth_mac_addr(net, p);
+       if (ret < 0)
+               return ret;
+
+       /* Set the MAC address */
+       return aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+                               ETH_ALEN, net->dev_addr);
+}
+
+static int aqc111_vlan_rx_kill_vid(struct net_device *net,
+                                  __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u8 vlan_ctrl = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       vlan_ctrl = reg8;
+
+       /* Address */
+       reg8 = (vid / 16);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+       /* Data */
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg16 &= ~(1 << (vid % 16));
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+       return 0;
+}
+
+static int aqc111_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u8 vlan_ctrl = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       vlan_ctrl = reg8;
+
+       /* Address */
+       reg8 = (vid / 16);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+       /* Data */
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg16 |= (1 << (vid % 16));
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+       reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+       return 0;
+}
+
+static void aqc111_set_rx_mode(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       int mc_count = 0;
+
+       mc_count = netdev_mc_count(net);
+
+       aqc111_data->rxctl &= ~(SFR_RX_CTL_PRO | SFR_RX_CTL_AMALL |
+                               SFR_RX_CTL_AM);
+
+       if (net->flags & IFF_PROMISC) {
+               aqc111_data->rxctl |= SFR_RX_CTL_PRO;
+       } else if ((net->flags & IFF_ALLMULTI) || mc_count > AQ_MAX_MCAST) {
+               aqc111_data->rxctl |= SFR_RX_CTL_AMALL;
+       } else if (!netdev_mc_empty(net)) {
+               u8 m_filter[AQ_MCAST_FILTER_SIZE] = { 0 };
+               struct netdev_hw_addr *ha = NULL;
+               u32 crc_bits = 0;
+
+               netdev_for_each_mc_addr(ha, net) {
+                       crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+                       m_filter[crc_bits >> 3] |= BIT(crc_bits & 7);
+               }
+
+               aqc111_write_cmd_async(dev, AQ_ACCESS_MAC,
+                                      SFR_MULTI_FILTER_ARRY,
+                                      AQ_MCAST_FILTER_SIZE,
+                                      AQ_MCAST_FILTER_SIZE, m_filter);
+
+               aqc111_data->rxctl |= SFR_RX_CTL_AM;
+       }
+
+       aqc111_write16_cmd_async(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                2, &aqc111_data->rxctl);
+}
+
+static int aqc111_set_features(struct net_device *net,
+                              netdev_features_t features)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       netdev_features_t changed = net->features ^ features;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       if (changed & NETIF_F_IP_CSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+               reg8 ^= SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+                                1, 1, &reg8);
+       }
+
+       if (changed & NETIF_F_IPV6_CSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+               reg8 ^= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+                                1, 1, &reg8);
+       }
+
+       if (changed & NETIF_F_RXCSUM) {
+               aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+               if (features & NETIF_F_RXCSUM) {
+                       aqc111_data->rx_checksum = 1;
+                       reg8 &= ~(SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                                 SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6);
+               } else {
+                       aqc111_data->rx_checksum = 0;
+                       reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                               SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+               }
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL,
+                                1, 1, &reg8);
+       }
+       if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+                       u16 i = 0;
+
+                       for (i = 0; i < 256; i++) {
+                               /* Address */
+                               reg8 = i;
+                               aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                                SFR_VLAN_ID_ADDRESS,
+                                                1, 1, &reg8);
+                               /* Data */
+                               aqc111_write16_cmd(dev, AQ_ACCESS_MAC,
+                                                  SFR_VLAN_ID_DATA0,
+                                                  2, &reg16);
+                               reg8 = SFR_VLAN_CONTROL_WE;
+                               aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                                SFR_VLAN_ID_CONTROL,
+                                                1, 1, &reg8);
+                       }
+                       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                       1, 1, &reg8);
+                       reg8 |= SFR_VLAN_CONTROL_VFE;
+                       aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                        SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+               } else {
+                       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                       1, 1, &reg8);
+                       reg8 &= ~SFR_VLAN_CONTROL_VFE;
+                       aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+                                        SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+               }
+       }
+
+       return 0;
+}
+
+static const struct net_device_ops aqc111_netdev_ops = {
+       .ndo_open               = usbnet_open,
+       .ndo_stop               = usbnet_stop,
+       .ndo_start_xmit         = usbnet_start_xmit,
+       .ndo_tx_timeout         = usbnet_tx_timeout,
+       .ndo_get_stats64        = usbnet_get_stats64,
+       .ndo_change_mtu         = aqc111_change_mtu,
+       .ndo_set_mac_address    = aqc111_set_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_vlan_rx_add_vid    = aqc111_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid   = aqc111_vlan_rx_kill_vid,
+       .ndo_set_rx_mode        = aqc111_set_rx_mode,
+       .ndo_set_features       = aqc111_set_features,
+};
+
+static int aqc111_read_perm_mac(struct usbnet *dev)
+{
+       u8 buf[ETH_ALEN];
+       int ret;
+
+       ret = aqc111_read_cmd(dev, AQ_FLASH_PARAMETERS, 0, 0, ETH_ALEN, buf);
+       if (ret < 0)
+               goto out;
+
+       ether_addr_copy(dev->net->perm_addr, buf);
+
+       return 0;
+out:
+       return ret;
+}
+
+static void aqc111_read_fw_version(struct usbnet *dev,
+                                  struct aqc111_data *aqc111_data)
+{
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MAJOR,
+                       1, 1, &aqc111_data->fw_ver.major);
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MINOR,
+                       1, 1, &aqc111_data->fw_ver.minor);
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_REV,
+                       1, 1, &aqc111_data->fw_ver.rev);
+
+       if (aqc111_data->fw_ver.major & 0x80)
+               aqc111_data->fw_ver.major &= ~0x80;
+}
+
+static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       enum usb_device_speed usb_speed = udev->speed;
+       struct aqc111_data *aqc111_data;
+       int ret;
+
+       /* Check if vendor configuration */
+       if (udev->actconfig->desc.bConfigurationValue != 1) {
+               usb_driver_set_configuration(udev, 1);
+               return -ENODEV;
+       }
+
+       usb_reset_configuration(dev->udev);
+
+       ret = usbnet_get_endpoints(dev, intf);
+       if (ret < 0) {
+               netdev_dbg(dev->net, "usbnet_get_endpoints failed");
+               return ret;
+       }
+
+       aqc111_data = kzalloc(sizeof(*aqc111_data), GFP_KERNEL);
+       if (!aqc111_data)
+               return -ENOMEM;
+
+       /* store aqc111_data pointer in device data field */
+       dev->driver_priv = aqc111_data;
+
+       /* Init the MAC address */
+       ret = aqc111_read_perm_mac(dev);
+       if (ret)
+               goto out;
+
+       ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
+
+       /* Set Rx urb size */
+       dev->rx_urb_size = URB_SIZE;
+
+       /* Set TX needed headroom & tailroom */
+       dev->net->needed_headroom += sizeof(u64);
+       dev->net->needed_tailroom += sizeof(u64);
+
+       dev->net->max_mtu = 16334;
+
+       dev->net->netdev_ops = &aqc111_netdev_ops;
+       dev->net->ethtool_ops = &aqc111_ethtool_ops;
+
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+       dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
+
+       netif_set_gso_max_size(dev->net, 65535);
+
+       aqc111_read_fw_version(dev, aqc111_data);
+       aqc111_data->autoneg = AUTONEG_ENABLE;
+       aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
+                                        SPEED_5000 : SPEED_1000;
+
+       return 0;
+
+out:
+       kfree(aqc111_data);
+       return ret;
+}
+
+static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16;
+
+       /* Force bz */
+       reg16 = SFR_PHYPWR_RSTCTL_BZ;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+       reg16 = 0;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+
+       /* Power down ethernet PHY */
+       aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+       aqc111_data->phy_cfg |= AQ_LOW_POWER;
+       aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN;
+       aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0,
+                               &aqc111_data->phy_cfg);
+
+       kfree(aqc111_data);
+}
+
+static void aqc111_status(struct usbnet *dev, struct urb *urb)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u64 *event_data = NULL;
+       int link = 0;
+
+       if (urb->actual_length < sizeof(*event_data))
+               return;
+
+       event_data = urb->transfer_buffer;
+       le64_to_cpus(event_data);
+
+       if (*event_data & AQ_LS_MASK)
+               link = 1;
+       else
+               link = 0;
+
+       aqc111_data->link_speed = (*event_data & AQ_SPEED_MASK) >>
+                                 AQ_SPEED_SHIFT;
+       aqc111_data->link = link;
+
+       if (netif_carrier_ok(dev->net) != link)
+               usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+}
+
+static void aqc111_configure_rx(struct usbnet *dev,
+                               struct aqc111_data *aqc111_data)
+{
+       enum usb_device_speed usb_speed = dev->udev->speed;
+       u16 link_speed = 0, usb_host = 0;
+       u8 buf[5] = { 0 };
+       u8 queue_num = 0;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       buf[0] = 0x00;
+       buf[1] = 0xF8;
+       buf[2] = 0x07;
+       switch (aqc111_data->link_speed) {
+       case AQ_INT_SPEED_5G:
+               link_speed = 5000;
+               reg8 = 0x05;
+               reg16 = 0x001F;
+               break;
+       case AQ_INT_SPEED_2_5G:
+               link_speed = 2500;
+               reg16 = 0x003F;
+               break;
+       case AQ_INT_SPEED_1G:
+               link_speed = 1000;
+               reg16 = 0x009F;
+               break;
+       case AQ_INT_SPEED_100M:
+               link_speed = 100;
+               queue_num = 1;
+               reg16 = 0x063F;
+               buf[1] = 0xFB;
+               buf[2] = 0x4;
+               break;
+       }
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_INTER_PACKET_GAP_0,
+                        1, 1, &reg8);
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TX_PAUSE_RESEND_T, 3, 3, buf);
+
+       switch (usb_speed) {
+       case USB_SPEED_SUPER:
+               usb_host = 3;
+               break;
+       case USB_SPEED_HIGH:
+               usb_host = 2;
+               break;
+       case USB_SPEED_FULL:
+       case USB_SPEED_LOW:
+               usb_host = 1;
+               queue_num = 0;
+               break;
+       default:
+               usb_host = 0;
+               break;
+       }
+
+       if (dev->net->mtu > 12500 && dev->net->mtu <= 16334)
+               queue_num = 2; /* For Jumbo packet 16KB */
+
+       memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5);
+       /* RX bulk configuration */
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf);
+
+       /* Set high low water level */
+       if (dev->net->mtu <= 4500)
+               reg16 = 0x0810;
+       else if (dev->net->mtu <= 9500)
+               reg16 = 0x1020;
+       else if (dev->net->mtu <= 12500)
+               reg16 = 0x1420;
+       else if (dev->net->mtu <= 16334)
+               reg16 = 0x1A20;
+
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+                          2, &reg16);
+       netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host);
+}
+
+static void aqc111_configure_csum_offload(struct usbnet *dev)
+{
+       u8 reg8 = 0;
+
+       if (dev->net->features & NETIF_F_RXCSUM) {
+               reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+                       SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+       }
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+
+       reg8 = 0;
+       if (dev->net->features & NETIF_F_IP_CSUM)
+               reg8 |= SFR_TXCOE_IP | SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+
+       if (dev->net->features & NETIF_F_IPV6_CSUM)
+               reg8 |= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+}
+
+static int aqc111_link_reset(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16 = 0;
+       u8 reg8 = 0;
+
+       if (aqc111_data->link == 1) { /* Link up */
+               aqc111_configure_rx(dev, aqc111_data);
+
+               /* Vlan Tag Filter */
+               reg8 = SFR_VLAN_CONTROL_VSO;
+               if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+                       reg8 |= SFR_VLAN_CONTROL_VFE;
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+                                1, 1, &reg8);
+
+               reg8 = 0x0;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+                                1, 1, &reg8);
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMTX_DMA_CONTROL,
+                                1, 1, &reg8);
+
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, &reg8);
+
+               reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB;
+               aqc111_data->rxctl = reg16;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+               reg8 = SFR_RX_PATH_READY;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                                1, 1, &reg8);
+
+               reg8 = SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+
+               reg16 = 0;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               reg16 = SFR_MEDIUM_XGMIIMODE | SFR_MEDIUM_FULL_DUPLEX;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_configure_csum_offload(dev);
+
+               aqc111_set_rx_mode(dev->net);
+
+               aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                 2, &reg16);
+
+               if (dev->net->mtu > 1500)
+                       reg16 |= SFR_MEDIUM_JUMBO_EN;
+
+               reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN |
+                        SFR_MEDIUM_TXFLOW_CTRLEN;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_data->rxctl |= SFR_RX_CTL_START;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                  2, &aqc111_data->rxctl);
+
+               netif_carrier_on(dev->net);
+       } else {
+               aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                 2, &reg16);
+               reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                                  2, &reg16);
+
+               aqc111_data->rxctl &= ~SFR_RX_CTL_START;
+               aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                  2, &aqc111_data->rxctl);
+
+               reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+               reg8 = SFR_BULK_OUT_EFF_EN;
+               aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                                1, 1, &reg8);
+
+               netif_carrier_off(dev->net);
+       }
+       return 0;
+}
+
+static int aqc111_reset(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u8 reg8 = 0;
+
+       dev->rx_urb_size = URB_SIZE;
+
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+       dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
+
+       /* Power up ethernet PHY */
+       aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                          &aqc111_data->phy_cfg);
+
+       /* Set the MAC address */
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+                        ETH_ALEN, dev->net->dev_addr);
+
+       reg8 = 0xFF;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, &reg8);
+
+       reg8 = 0x0;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_SWP_CTRL, 1, 1, &reg8);
+
+       aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+       reg8 &= ~(SFR_MONITOR_MODE_EPHYRW | SFR_MONITOR_MODE_RWLC |
+                 SFR_MONITOR_MODE_RWMP | SFR_MONITOR_MODE_RWWF |
+                 SFR_MONITOR_MODE_RW_FLAG);
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+
+       netif_carrier_off(dev->net);
+
+       /* Phy advertise */
+       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                            aqc111_data->advertised_speed);
+
+       return 0;
+}
+
+static int aqc111_stop(struct usbnet *dev)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16 = 0;
+
+       aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                         2, &reg16);
+       reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                          2, &reg16);
+       reg16 = 0;
+       aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       /* Put PHY to low power*/
+       aqc111_data->phy_cfg |= AQ_LOW_POWER;
+       aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                          &aqc111_data->phy_cfg);
+
+       netif_carrier_off(dev->net);
+
+       return 0;
+}
+
+static void aqc111_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+       u32 pkt_type = 0;
+
+       skb->ip_summed = CHECKSUM_NONE;
+       /* checksum error bit is set */
+       if (pkt_desc & AQ_RX_PD_L4_ERR || pkt_desc & AQ_RX_PD_L3_ERR)
+               return;
+
+       pkt_type = pkt_desc & AQ_RX_PD_L4_TYPE_MASK;
+       /* It must be a TCP or UDP packet with a valid checksum */
+       if (pkt_type == AQ_RX_PD_L4_TCP || pkt_type == AQ_RX_PD_L4_UDP)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       struct sk_buff *new_skb = NULL;
+       u32 pkt_total_offset = 0;
+       u64 *pkt_desc_ptr = NULL;
+       u32 start_of_descs = 0;
+       u32 desc_offset = 0; /*RX Header Offset*/
+       u16 pkt_count = 0;
+       u64 desc_hdr = 0;
+       u16 vlan_tag = 0;
+       u32 skb_len = 0;
+
+       if (!skb)
+               goto err;
+
+       if (skb->len == 0)
+               goto err;
+
+       skb_len = skb->len;
+       /* RX Descriptor Header */
+       skb_trim(skb, skb->len - sizeof(desc_hdr));
+       desc_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb));
+
+       /* Check these packets */
+       desc_offset = (desc_hdr & AQ_RX_DH_DESC_OFFSET_MASK) >>
+                     AQ_RX_DH_DESC_OFFSET_SHIFT;
+       pkt_count = desc_hdr & AQ_RX_DH_PKT_CNT_MASK;
+       start_of_descs = skb_len - ((pkt_count + 1) *  sizeof(desc_hdr));
+
+       /* self check descs position */
+       if (start_of_descs != desc_offset)
+               goto err;
+
+       /* self check desc_offset from header*/
+       if (desc_offset >= skb_len)
+               goto err;
+
+       if (pkt_count == 0)
+               goto err;
+
+       /* Get the first RX packet descriptor */
+       pkt_desc_ptr = (u64 *)(skb->data + desc_offset);
+
+       while (pkt_count--) {
+               u64 pkt_desc = le64_to_cpup(pkt_desc_ptr);
+               u32 pkt_len_with_padd = 0;
+               u32 pkt_len = 0;
+
+               pkt_len = (u32)((pkt_desc & AQ_RX_PD_LEN_MASK) >>
+                         AQ_RX_PD_LEN_SHIFT);
+               pkt_len_with_padd = ((pkt_len + 7) & 0x7FFF8);
+
+               pkt_total_offset += pkt_len_with_padd;
+               if (pkt_total_offset > desc_offset ||
+                   (pkt_count == 0 && pkt_total_offset != desc_offset)) {
+                       goto err;
+               }
+
+               if (pkt_desc & AQ_RX_PD_DROP ||
+                   !(pkt_desc & AQ_RX_PD_RX_OK) ||
+                   pkt_len > (dev->hard_mtu + AQ_RX_HW_PAD)) {
+                       skb_pull(skb, pkt_len_with_padd);
+                       /* Next RX Packet Descriptor */
+                       pkt_desc_ptr++;
+                       continue;
+               }
+
+               /* Clone SKB */
+               new_skb = skb_clone(skb, GFP_ATOMIC);
+
+               if (!new_skb)
+                       goto err;
+
+               new_skb->len = pkt_len;
+               skb_pull(new_skb, AQ_RX_HW_PAD);
+               skb_set_tail_pointer(new_skb, new_skb->len);
+
+               new_skb->truesize = SKB_TRUESIZE(new_skb->len);
+               if (aqc111_data->rx_checksum)
+                       aqc111_rx_checksum(new_skb, pkt_desc);
+
+               if (pkt_desc & AQ_RX_PD_VLAN) {
+                       vlan_tag = pkt_desc >> AQ_RX_PD_VLAN_SHIFT;
+                       __vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
+                                              vlan_tag & VLAN_VID_MASK);
+               }
+
+               usbnet_skb_return(dev, new_skb);
+               if (pkt_count == 0)
+                       break;
+
+               skb_pull(skb, pkt_len_with_padd);
+
+               /* Next RX Packet Header */
+               pkt_desc_ptr++;
+
+               new_skb = NULL;
+       }
+
+       return 1;
+
+err:
+       return 0;
+}
+
+static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                      gfp_t flags)
+{
+       int frame_size = dev->maxpacket;
+       struct sk_buff *new_skb = NULL;
+       u64 *tx_desc_ptr = NULL;
+       int padding_size = 0;
+       int headroom = 0;
+       int tailroom = 0;
+       u64 tx_desc = 0;
+       u16 tci = 0;
+
+       /*Length of actual data*/
+       tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
+
+       /* TSO MSS */
+       tx_desc |= ((u64)(skb_shinfo(skb)->gso_size & AQ_TX_DESC_MSS_MASK)) <<
+                  AQ_TX_DESC_MSS_SHIFT;
+
+       headroom = (skb->len + sizeof(tx_desc)) % 8;
+       if (headroom != 0)
+               padding_size = 8 - headroom;
+
+       if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+               padding_size += 8;
+               tx_desc |= AQ_TX_DESC_DROP_PADD;
+       }
+
+       /* Vlan Tag */
+       if (vlan_get_tag(skb, &tci) >= 0) {
+               tx_desc |= AQ_TX_DESC_VLAN;
+               tx_desc |= ((u64)tci & AQ_TX_DESC_VLAN_MASK) <<
+                          AQ_TX_DESC_VLAN_SHIFT;
+       }
+
+       if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) &&
+           skb_linearize(skb))
+               return NULL;
+
+       headroom = skb_headroom(skb);
+       tailroom = skb_tailroom(skb);
+
+       if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+               new_skb = skb_copy_expand(skb, sizeof(tx_desc),
+                                         padding_size, flags);
+               dev_kfree_skb_any(skb);
+               skb = new_skb;
+               if (!skb)
+                       return NULL;
+       }
+       if (padding_size != 0)
+               skb_put_zero(skb, padding_size);
+       /* Copy TX header */
+       tx_desc_ptr = skb_push(skb, sizeof(tx_desc));
+       *tx_desc_ptr = cpu_to_le64(tx_desc);
+
+       usbnet_set_skb_tx_stats(skb, 1, 0);
+
+       return skb;
+}
+
+static const struct driver_info aqc111_info = {
+       .description    = "Aquantia AQtion USB to 5GbE Controller",
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#define ASIX111_DESC \
+"ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix111_info = {
+       .description    = ASIX111_DESC,
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#undef ASIX111_DESC
+
+#define ASIX112_DESC \
+"ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix112_info = {
+       .description    = ASIX112_DESC,
+       .bind           = aqc111_bind,
+       .unbind         = aqc111_unbind,
+       .status         = aqc111_status,
+       .link_reset     = aqc111_link_reset,
+       .reset          = aqc111_reset,
+       .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .rx_fixup       = aqc111_rx_fixup,
+       .tx_fixup       = aqc111_tx_fixup,
+};
+
+#undef ASIX112_DESC
+
+static int aqc111_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usbnet *dev = usb_get_intfdata(intf);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 temp_rx_ctrl = 0x00;
+       u16 reg16;
+       u8 reg8;
+
+       usbnet_suspend(intf, message);
+
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+       temp_rx_ctrl = reg16;
+       /* Stop RX operations*/
+       reg16 &= ~SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+       /* Force bz */
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                              2, &reg16);
+       reg16 |= SFR_PHYPWR_RSTCTL_BZ;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+                               2, &reg16);
+
+       reg8 = SFR_BULK_OUT_EFF_EN;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+                             1, 1, &reg8);
+
+       temp_rx_ctrl &= ~(SFR_RX_CTL_START | SFR_RX_CTL_RF_WAK |
+                         SFR_RX_CTL_AP | SFR_RX_CTL_AM);
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                               2, &temp_rx_ctrl);
+
+       reg8 = 0x00;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                             1, 1, &reg8);
+
+       if (aqc111_data->wol_flags) {
+               struct aqc111_wol_cfg wol_cfg;
+
+               memset(&wol_cfg, 0, sizeof(struct aqc111_wol_cfg));
+
+               aqc111_data->phy_cfg |= AQ_WOL;
+               ether_addr_copy(wol_cfg.hw_addr, dev->net->dev_addr);
+               wol_cfg.flags = aqc111_data->wol_flags;
+
+               temp_rx_ctrl |= (SFR_RX_CTL_AB | SFR_RX_CTL_START);
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+                                       2, &temp_rx_ctrl);
+               reg8 = 0x00;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+                                     1, 1, &reg8);
+               reg8 = SFR_BMRX_DMA_EN;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+                                     1, 1, &reg8);
+               reg8 = SFR_RX_PATH_READY;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                                     1, 1, &reg8);
+               reg8 = 0x07;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+                                     1, 1, &reg8);
+               reg8 = 0x00;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                     SFR_RX_BULKIN_QTIMR_LOW, 1, 1, &reg8);
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                     SFR_RX_BULKIN_QTIMR_HIGH, 1, 1, &reg8);
+               reg8 = 0xFF;
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QSIZE,
+                                     1, 1, &reg8);
+               aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QIFG,
+                                     1, 1, &reg8);
+
+               aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                      SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+               reg16 |= SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+
+               aqc111_write_cmd(dev, AQ_WOL_CFG, 0, 0,
+                                WOL_CFG_SIZE, &wol_cfg);
+               aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                                  &aqc111_data->phy_cfg);
+       } else {
+               aqc111_data->phy_cfg |= AQ_LOW_POWER;
+               aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+                                  &aqc111_data->phy_cfg);
+
+               /* Disable RX path */
+               aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                      SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+               reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+               aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+                                       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+       }
+
+       return 0;
+}
+
+static int aqc111_resume(struct usb_interface *intf)
+{
+       struct usbnet *dev = usb_get_intfdata(intf);
+       struct aqc111_data *aqc111_data = dev->driver_priv;
+       u16 reg16;
+       u8 reg8;
+
+       netif_carrier_off(dev->net);
+
+       /* Power up ethernet PHY */
+       aqc111_data->phy_cfg |= AQ_PHY_POWER_EN;
+       aqc111_data->phy_cfg &= ~AQ_LOW_POWER;
+       aqc111_data->phy_cfg &= ~AQ_WOL;
+
+       reg8 = 0xFF;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+                             1, 1, &reg8);
+       /* Configure RX control register => start operation */
+       reg16 = aqc111_data->rxctl;
+       reg16 &= ~SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       reg16 |= SFR_RX_CTL_START;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+       aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+                            aqc111_data->advertised_speed);
+
+       aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                              2, &reg16);
+       reg16 |= SFR_MEDIUM_RECEIVE_EN;
+       aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+                               2, &reg16);
+       reg8 = SFR_RX_PATH_READY;
+       aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+                             1, 1, &reg8);
+       reg8 = 0x0;
+       aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, 1, 1, &reg8);
+
+       return usbnet_resume(intf);
+}
+
+#define AQC111_USB_ETH_DEV(vid, pid, table) \
+       USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_VENDOR_SPEC), \
+       .driver_info = (unsigned long)&(table) \
+}, \
+{ \
+       USB_DEVICE_AND_INTERFACE_INFO((vid), (pid), \
+                                     USB_CLASS_COMM, \
+                                     USB_CDC_SUBCLASS_ETHERNET, \
+                                     USB_CDC_PROTO_NONE), \
+       .driver_info = (unsigned long)&(table),
+
+static const struct usb_device_id products[] = {
+       {AQC111_USB_ETH_DEV(0x2eca, 0xc101, aqc111_info)},
+       {AQC111_USB_ETH_DEV(0x0b95, 0x2790, asix111_info)},
+       {AQC111_USB_ETH_DEV(0x0b95, 0x2791, asix112_info)},
+       { },/* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver aq_driver = {
+       .name           = "aqc111",
+       .id_table       = products,
+       .probe          = usbnet_probe,
+       .suspend        = aqc111_suspend,
+       .resume         = aqc111_resume,
+       .disconnect     = usbnet_disconnect,
+};
+
+module_usb_driver(aq_driver);
+
+MODULE_DESCRIPTION("Aquantia AQtion USB to 5/2.5GbE Controllers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/aqc111.h b/drivers/net/usb/aqc111.h
new file mode 100644 (file)
index 0000000..4d68b3a
--- /dev/null
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Aquantia Corp. Aquantia AQtion USB to 5GbE Controller
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2002-2003 TiVo Inc.
+ * Copyright (C) 2017-2018 ASIX
+ * Copyright (C) 2018 Aquantia Corp.
+ */
+
+#ifndef __LINUX_USBNET_AQC111_H
+#define __LINUX_USBNET_AQC111_H
+
+#define URB_SIZE       (1024 * 62)
+
+#define AQ_MCAST_FILTER_SIZE           8
+#define AQ_MAX_MCAST                   64
+
+#define AQ_ACCESS_MAC                  0x01
+#define AQ_FLASH_PARAMETERS            0x20
+#define AQ_PHY_POWER                   0x31
+#define AQ_WOL_CFG                     0x60
+#define AQ_PHY_OPS                     0x61
+
+#define AQ_USB_PHY_SET_TIMEOUT         10000
+#define AQ_USB_SET_TIMEOUT             4000
+
+/* Feature. ********************************************/
+#define AQ_SUPPORT_FEATURE     (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX |\
+                                NETIF_F_HW_VLAN_CTAG_RX)
+
+#define AQ_SUPPORT_HW_FEATURE  (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_FILTER)
+
+#define AQ_SUPPORT_VLAN_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\
+                                NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+                                NETIF_F_TSO)
+
+/* SFR Reg. ********************************************/
+
+#define SFR_GENERAL_STATUS             0x03
+#define SFR_CHIP_STATUS                        0x05
+#define SFR_RX_CTL                     0x0B
+       #define SFR_RX_CTL_TXPADCRC             0x0400
+       #define SFR_RX_CTL_IPE                  0x0200
+       #define SFR_RX_CTL_DROPCRCERR           0x0100
+       #define SFR_RX_CTL_START                0x0080
+       #define SFR_RX_CTL_RF_WAK               0x0040
+       #define SFR_RX_CTL_AP                   0x0020
+       #define SFR_RX_CTL_AM                   0x0010
+       #define SFR_RX_CTL_AB                   0x0008
+       #define SFR_RX_CTL_AMALL                0x0002
+       #define SFR_RX_CTL_PRO                  0x0001
+       #define SFR_RX_CTL_STOP                 0x0000
+#define SFR_INTER_PACKET_GAP_0         0x0D
+#define SFR_NODE_ID                    0x10
+#define SFR_MULTI_FILTER_ARRY          0x16
+#define SFR_MEDIUM_STATUS_MODE         0x22
+       #define SFR_MEDIUM_XGMIIMODE            0x0001
+       #define SFR_MEDIUM_FULL_DUPLEX          0x0002
+       #define SFR_MEDIUM_RXFLOW_CTRLEN        0x0010
+       #define SFR_MEDIUM_TXFLOW_CTRLEN        0x0020
+       #define SFR_MEDIUM_JUMBO_EN             0x0040
+       #define SFR_MEDIUM_RECEIVE_EN           0x0100
+#define SFR_MONITOR_MODE               0x24
+       #define SFR_MONITOR_MODE_EPHYRW         0x01
+       #define SFR_MONITOR_MODE_RWLC           0x02
+       #define SFR_MONITOR_MODE_RWMP           0x04
+       #define SFR_MONITOR_MODE_RWWF           0x08
+       #define SFR_MONITOR_MODE_RW_FLAG        0x10
+       #define SFR_MONITOR_MODE_PMEPOL         0x20
+       #define SFR_MONITOR_MODE_PMETYPE        0x40
+#define SFR_PHYPWR_RSTCTL              0x26
+       #define SFR_PHYPWR_RSTCTL_BZ            0x0010
+       #define SFR_PHYPWR_RSTCTL_IPRL          0x0020
+#define SFR_VLAN_ID_ADDRESS            0x2A
+#define SFR_VLAN_ID_CONTROL            0x2B
+       #define SFR_VLAN_CONTROL_WE             0x0001
+       #define SFR_VLAN_CONTROL_RD             0x0002
+       #define SFR_VLAN_CONTROL_VSO            0x0010
+       #define SFR_VLAN_CONTROL_VFE            0x0020
+#define SFR_VLAN_ID_DATA0              0x2C
+#define SFR_VLAN_ID_DATA1              0x2D
+#define SFR_RX_BULKIN_QCTRL            0x2E
+       #define SFR_RX_BULKIN_QCTRL_TIME        0x01
+       #define SFR_RX_BULKIN_QCTRL_IFG         0x02
+       #define SFR_RX_BULKIN_QCTRL_SIZE        0x04
+#define SFR_RX_BULKIN_QTIMR_LOW                0x2F
+#define SFR_RX_BULKIN_QTIMR_HIGH       0x30
+#define SFR_RX_BULKIN_QSIZE            0x31
+#define SFR_RX_BULKIN_QIFG             0x32
+#define SFR_RXCOE_CTL                  0x34
+       #define SFR_RXCOE_IP                    0x01
+       #define SFR_RXCOE_TCP                   0x02
+       #define SFR_RXCOE_UDP                   0x04
+       #define SFR_RXCOE_ICMP                  0x08
+       #define SFR_RXCOE_IGMP                  0x10
+       #define SFR_RXCOE_TCPV6                 0x20
+       #define SFR_RXCOE_UDPV6                 0x40
+       #define SFR_RXCOE_ICMV6                 0x80
+#define SFR_TXCOE_CTL                  0x35
+       #define SFR_TXCOE_IP                    0x01
+       #define SFR_TXCOE_TCP                   0x02
+       #define SFR_TXCOE_UDP                   0x04
+       #define SFR_TXCOE_ICMP                  0x08
+       #define SFR_TXCOE_IGMP                  0x10
+       #define SFR_TXCOE_TCPV6                 0x20
+       #define SFR_TXCOE_UDPV6                 0x40
+       #define SFR_TXCOE_ICMV6                 0x80
+#define SFR_BM_INT_MASK                        0x41
+#define SFR_BMRX_DMA_CONTROL           0x43
+       #define SFR_BMRX_DMA_EN                 0x80
+#define SFR_BMTX_DMA_CONTROL           0x46
+#define SFR_PAUSE_WATERLVL_LOW         0x54
+#define SFR_PAUSE_WATERLVL_HIGH                0x55
+#define SFR_ARC_CTRL                   0x9E
+#define SFR_SWP_CTRL                   0xB1
+#define SFR_TX_PAUSE_RESEND_T          0xB2
+#define SFR_ETH_MAC_PATH               0xB7
+       #define SFR_RX_PATH_READY               0x01
+#define SFR_BULK_OUT_CTRL              0xB9
+       #define SFR_BULK_OUT_FLUSH_EN           0x01
+       #define SFR_BULK_OUT_EFF_EN             0x02
+
+#define AQ_FW_VER_MAJOR                        0xDA
+#define AQ_FW_VER_MINOR                        0xDB
+#define AQ_FW_VER_REV                  0xDC
+
+/*PHY_OPS**********************************************************************/
+
+#define AQ_ADV_100M    BIT(0)
+#define AQ_ADV_1G      BIT(1)
+#define AQ_ADV_2G5     BIT(2)
+#define AQ_ADV_5G      BIT(3)
+#define AQ_ADV_MASK    0x0F
+
+#define AQ_PAUSE       BIT(16)
+#define AQ_ASYM_PAUSE  BIT(17)
+#define AQ_LOW_POWER   BIT(18)
+#define AQ_PHY_POWER_EN        BIT(19)
+#define AQ_WOL         BIT(20)
+#define AQ_DOWNSHIFT   BIT(21)
+
+#define AQ_DSH_RETRIES_SHIFT   0x18
+#define AQ_DSH_RETRIES_MASK    0xF000000
+
+#define AQ_WOL_FLAG_MP                 0x2
+
+/******************************************************************************/
+
+struct aqc111_wol_cfg {
+       u8 hw_addr[6];
+       u8 flags;
+       u8 rsvd[283];
+} __packed;
+
+#define WOL_CFG_SIZE sizeof(struct aqc111_wol_cfg)
+
+struct aqc111_data {
+       u16 rxctl;
+       u8 rx_checksum;
+       u8 link_speed;
+       u8 link;
+       u8 autoneg;
+       u32 advertised_speed;
+       struct {
+               u8 major;
+               u8 minor;
+               u8 rev;
+       } fw_ver;
+       u32 phy_cfg;
+       u8 wol_flags;
+};
+
+#define AQ_LS_MASK             0x8000
+#define AQ_SPEED_MASK          0x7F00
+#define AQ_SPEED_SHIFT         0x0008
+#define AQ_INT_SPEED_5G                0x000F
+#define AQ_INT_SPEED_2_5G      0x0010
+#define AQ_INT_SPEED_1G                0x0011
+#define AQ_INT_SPEED_100M      0x0013
+
+/* TX Descriptor */
+#define AQ_TX_DESC_LEN_MASK    0x1FFFFF
+#define AQ_TX_DESC_DROP_PADD   BIT(28)
+#define AQ_TX_DESC_VLAN                BIT(29)
+#define AQ_TX_DESC_MSS_MASK    0x7FFF
+#define AQ_TX_DESC_MSS_SHIFT   0x20
+#define AQ_TX_DESC_VLAN_MASK   0xFFFF
+#define AQ_TX_DESC_VLAN_SHIFT  0x30
+
+#define AQ_RX_HW_PAD                   0x02
+
+/* RX Packet Descriptor */
+#define AQ_RX_PD_L4_ERR                BIT(0)
+#define AQ_RX_PD_L3_ERR                BIT(1)
+#define AQ_RX_PD_L4_TYPE_MASK  0x1C
+#define AQ_RX_PD_L4_UDP                0x04
+#define AQ_RX_PD_L4_TCP                0x10
+#define AQ_RX_PD_L3_TYPE_MASK  0x60
+#define AQ_RX_PD_L3_IP         0x20
+#define AQ_RX_PD_L3_IP6                0x40
+
+#define AQ_RX_PD_VLAN          BIT(10)
+#define AQ_RX_PD_RX_OK         BIT(11)
+#define AQ_RX_PD_DROP          BIT(31)
+#define AQ_RX_PD_LEN_MASK      0x7FFF0000
+#define AQ_RX_PD_LEN_SHIFT     0x10
+#define AQ_RX_PD_VLAN_SHIFT    0x20
+
+/* RX Descriptor header */
+#define AQ_RX_DH_PKT_CNT_MASK          0x1FFF
+#define AQ_RX_DH_DESC_OFFSET_MASK      0xFFFFE000
+#define AQ_RX_DH_DESC_OFFSET_SHIFT     0x0D
+
+static struct {
+       unsigned char ctrl;
+       unsigned char timer_l;
+       unsigned char timer_h;
+       unsigned char size;
+       unsigned char ifg;
+} AQC111_BULKIN_SIZE[] = {
+       /* xHCI & EHCI & OHCI */
+       {7, 0x00, 0x01, 0x1E, 0xFF},/* 10G, 5G, 2.5G, 1G */
+       {7, 0xA0, 0x00, 0x14, 0x00},/* 100M */
+       /* Jumbo packet */
+       {7, 0x00, 0x01, 0x18, 0xFF},
+};
+
+#endif /* __LINUX_USBNET_AQC111_H */
index 5c42cf8..b3b3c05 100644 (file)
@@ -562,6 +562,8 @@ static const struct driver_info wwan_info = {
 #define MICROSOFT_VENDOR_ID    0x045e
 #define UBLOX_VENDOR_ID                0x1546
 #define TPLINK_VENDOR_ID       0x2357
+#define AQUANTIA_VENDOR_ID     0x2eca
+#define ASIX_VENDOR_ID         0x0b95
 
 static const struct usb_device_id      products[] = {
 /* BLACKLIST !!
@@ -821,6 +823,30 @@ static const struct usb_device_id  products[] = {
        .driver_info = 0,
 },
 
+/* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(AQUANTIA_VENDOR_ID, 0xc101,
+                                     USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter(based on AQC111U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2790, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter(based on AQC112U) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2791, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
 /* WHITELIST!!!
  *
  * CDC Ether uses two interfaces, not necessarily consecutive.
index be1917b..3c8bdac 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/if_vlan.h>
 #include <linux/uaccess.h>
+#include <linux/linkmode.h>
 #include <linux/list.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
@@ -1586,18 +1587,17 @@ static int lan78xx_set_pause(struct net_device *net,
                dev->fc_request_control |= FLOW_CTRL_TX;
 
        if (ecmd.base.autoneg) {
+               __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
                u32 mii_adv;
-               u32 advertising;
 
-               ethtool_convert_link_mode_to_legacy_u32(
-                       &advertising, ecmd.link_modes.advertising);
-
-               advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                  ecmd.link_modes.advertising);
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                  ecmd.link_modes.advertising);
                mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
-               advertising |= mii_adv_to_ethtool_adv_t(mii_adv);
-
-               ethtool_convert_legacy_u32_to_link_mode(
-                       ecmd.link_modes.advertising, advertising);
+               mii_adv_to_linkmode_adv_t(fc, mii_adv);
+               linkmode_or(ecmd.link_modes.advertising, fc,
+                           ecmd.link_modes.advertising);
 
                phy_ethtool_ksettings_set(phydev, &ecmd);
        }
@@ -2095,6 +2095,7 @@ static struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
 
 static int lan78xx_phy_init(struct lan78xx_net *dev)
 {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
        int ret;
        u32 mii_adv;
        struct phy_device *phydev;
@@ -2158,9 +2159,13 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
 
        /* support both flow controls */
        dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX);
-       phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                          phydev->advertising);
+       linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                          phydev->advertising);
        mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
-       phydev->advertising |= mii_adv_to_ethtool_adv_t(mii_adv);
+       mii_adv_to_linkmode_adv_t(fc, mii_adv);
+       linkmode_or(phydev->advertising, fc, phydev->advertising);
 
        if (phydev->mdio.dev.of_node) {
                u32 reg;
index f2d01cb..e3d0862 100644 (file)
@@ -618,9 +618,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
                return;
        }
 
-       memcpy(&intdata, urb->transfer_buffer, 4);
-       le32_to_cpus(&intdata);
-
+       intdata = get_unaligned_le32(urb->transfer_buffer);
        netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
 
        if (intdata & INT_ENP_PHY_INT_)
@@ -1295,6 +1293,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
                dev->net->features |= NETIF_F_RXCSUM;
 
        dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+       set_bit(EVENT_NO_IP_ALIGN, &dev->flags);
 
        smsc95xx_init_mac_address(dev);
 
@@ -1933,8 +1932,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                unsigned char *packet;
                u16 size;
 
-               memcpy(&header, skb->data, sizeof(header));
-               le32_to_cpus(&header);
+               header = get_unaligned_le32(skb->data);
                skb_pull(skb, 4 + NET_IP_ALIGN);
                packet = skb->data;
 
@@ -2011,12 +2009,30 @@ static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
        return (high_16 << 16) | low_16;
 }
 
+/* The TX CSUM won't work if the checksum lies in the last 4 bytes of the
+ * transmission. This is fairly unlikely, only seems to trigger with some
+ * short TCP ACK packets sent.
+ *
+ * Note, this calculation should probably check for the alignment of the
+ * data as well, but a straight check for csum being in the last four bytes
+ * of the packet should be ok for now.
+ */
+static bool smsc95xx_can_tx_checksum(struct sk_buff *skb)
+{
+       unsigned int len = skb->len - skb_checksum_start_offset(skb);
+
+       if (skb->len <= 45)
+              return false;
+       return skb->csum_offset < (len - (4 + 1));
+}
+
 static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                                         struct sk_buff *skb, gfp_t flags)
 {
        bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
        int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
        u32 tx_cmd_a, tx_cmd_b;
+       void *ptr;
 
        /* We do not advertise SG, so skbs should be already linearized */
        BUG_ON(skb_shinfo(skb)->nr_frags);
@@ -2030,8 +2046,11 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                return NULL;
        }
 
+       tx_cmd_b = (u32)skb->len;
+       tx_cmd_a = tx_cmd_b | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+
        if (csum) {
-               if (skb->len <= 45) {
+               if (!smsc95xx_can_tx_checksum(skb)) {
                        /* workaround - hardware tx checksum does not work
                         * properly with extremely small packets */
                        long csstart = skb_checksum_start_offset(skb);
@@ -2043,24 +2062,18 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
                        csum = false;
                } else {
                        u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
-                       skb_push(skb, 4);
-                       cpu_to_le32s(&csum_preamble);
-                       memcpy(skb->data, &csum_preamble, 4);
+                       ptr = skb_push(skb, 4);
+                       put_unaligned_le32(csum_preamble, ptr);
+
+                       tx_cmd_a += 4;
+                       tx_cmd_b += 4;
+                       tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
                }
        }
 
-       skb_push(skb, 4);
-       tx_cmd_b = (u32)(skb->len - 4);
-       if (csum)
-               tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
-       cpu_to_le32s(&tx_cmd_b);
-       memcpy(skb->data, &tx_cmd_b, 4);
-
-       skb_push(skb, 4);
-       tx_cmd_a = (u32)(skb->len - 8) | TX_CMD_A_FIRST_SEG_ |
-               TX_CMD_A_LAST_SEG_;
-       cpu_to_le32s(&tx_cmd_a);
-       memcpy(skb->data, &tx_cmd_a, 4);
+       ptr = skb_push(skb, 8);
+       put_unaligned_le32(tx_cmd_a, ptr);
+       put_unaligned_le32(tx_cmd_b, ptr+4);
 
        return skb;
 }
index 890fa5b..f412ea1 100644 (file)
@@ -1253,7 +1253,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
                return PTR_ERR(net);
 
        peer = rtnl_create_link(net, ifname, name_assign_type,
-                               &veth_link_ops, tbp);
+                               &veth_link_ops, tbp, extack);
        if (IS_ERR(peer)) {
                put_net(net);
                return PTR_ERR(peer);
index 69b7227..95909e2 100644 (file)
@@ -747,7 +747,8 @@ static int vrf_rtable_create(struct net_device *dev)
 /**************************** device handling ********************/
 
 /* cycle interface to flush neighbor cache and move routes across tables */
-static void cycle_netdev(struct net_device *dev)
+static void cycle_netdev(struct net_device *dev,
+                        struct netlink_ext_ack *extack)
 {
        unsigned int flags = dev->flags;
        int ret;
@@ -755,9 +756,9 @@ static void cycle_netdev(struct net_device *dev)
        if (!netif_running(dev))
                return;
 
-       ret = dev_change_flags(dev, flags & ~IFF_UP);
+       ret = dev_change_flags(dev, flags & ~IFF_UP, extack);
        if (ret >= 0)
-               ret = dev_change_flags(dev, flags);
+               ret = dev_change_flags(dev, flags, extack);
 
        if (ret < 0) {
                netdev_err(dev,
@@ -785,7 +786,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
        if (ret < 0)
                goto err;
 
-       cycle_netdev(port_dev);
+       cycle_netdev(port_dev, extack);
 
        return 0;
 
@@ -815,7 +816,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
        netdev_upper_dev_unlink(port_dev, dev);
        port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
 
-       cycle_netdev(port_dev);
+       cycle_netdev(port_dev, NULL);
 
        return 0;
 }
@@ -981,24 +982,23 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
                                   struct sk_buff *skb)
 {
        int orig_iif = skb->skb_iif;
-       bool need_strict;
+       bool need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
+       bool is_ndisc = ipv6_ndisc_frame(skb);
 
-       /* loopback traffic; do not push through packet taps again.
-        * Reset pkt_type for upper layers to process skb
+       /* loopback, multicast & non-ND link-local traffic; do not push through
+        * packet taps again. Reset pkt_type for upper layers to process skb
         */
-       if (skb->pkt_type == PACKET_LOOPBACK) {
+       if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
                skb->dev = vrf_dev;
                skb->skb_iif = vrf_dev->ifindex;
                IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
-               skb->pkt_type = PACKET_HOST;
+               if (skb->pkt_type == PACKET_LOOPBACK)
+                       skb->pkt_type = PACKET_HOST;
                goto out;
        }
 
-       /* if packet is NDISC or addressed to multicast or link-local
-        * then keep the ingress interface
-        */
-       need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
-       if (!ipv6_ndisc_frame(skb) && !need_strict) {
+       /* if packet is NDISC then keep the ingress interface */
+       if (!is_ndisc) {
                vrf_rx_stats(vrf_dev, skb->len);
                skb->dev = vrf_dev;
                skb->skb_iif = vrf_dev->ifindex;
index 297cdea..b56ef68 100644 (file)
@@ -79,9 +79,11 @@ struct vxlan_fdb {
        u8                eth_addr[ETH_ALEN];
        u16               state;        /* see ndm_state */
        __be32            vni;
-       u8                flags;        /* see ndm_flags */
+       u16               flags;        /* see ndm_flags and below */
 };
 
+#define NTF_VXLAN_ADDED_BY_USER 0x100
+
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
 
@@ -186,7 +188,7 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
  * and enabled unshareable flags.
  */
 static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
-                                         __be16 port, u32 flags)
+                                         __be16 port, u32 flags, int ifindex)
 {
        struct vxlan_sock *vs;
 
@@ -195,7 +197,8 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
        hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
                if (inet_sk(vs->sock->sk)->inet_sport == port &&
                    vxlan_get_sk_family(vs) == family &&
-                   vs->flags == flags)
+                   vs->flags == flags &&
+                   vs->sock->sk->sk_bound_dev_if == ifindex)
                        return vs;
        }
        return NULL;
@@ -235,7 +238,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex,
 {
        struct vxlan_sock *vs;
 
-       vs = vxlan_find_sock(net, family, port, flags);
+       vs = vxlan_find_sock(net, family, port, flags, ifindex);
        if (!vs)
                return NULL;
 
@@ -355,6 +358,22 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
+static void vxlan_fdb_switchdev_notifier_info(const struct vxlan_dev *vxlan,
+                           const struct vxlan_fdb *fdb,
+                           const struct vxlan_rdst *rd,
+                           struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       fdb_info->info.dev = vxlan->dev;
+       fdb_info->remote_ip = rd->remote_ip;
+       fdb_info->remote_port = rd->remote_port;
+       fdb_info->remote_vni = rd->remote_vni;
+       fdb_info->remote_ifindex = rd->remote_ifindex;
+       memcpy(fdb_info->eth_addr, fdb->eth_addr, ETH_ALEN);
+       fdb_info->vni = fdb->vni;
+       fdb_info->offloaded = rd->offloaded;
+       fdb_info->added_by_user = fdb->flags & NTF_VXLAN_ADDED_BY_USER;
+}
+
 static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
                                               struct vxlan_fdb *fdb,
                                               struct vxlan_rdst *rd,
@@ -368,31 +387,25 @@ static void vxlan_fdb_switchdev_call_notifiers(struct vxlan_dev *vxlan,
 
        notifier_type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE
                               : SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE;
-
-       info = (struct switchdev_notifier_vxlan_fdb_info){
-               .remote_ip = rd->remote_ip,
-               .remote_port = rd->remote_port,
-               .remote_vni = rd->remote_vni,
-               .remote_ifindex = rd->remote_ifindex,
-               .vni = fdb->vni,
-               .offloaded = rd->offloaded,
-       };
-       memcpy(info.eth_addr, fdb->eth_addr, ETH_ALEN);
-
+       vxlan_fdb_switchdev_notifier_info(vxlan, fdb, rd, &info);
        call_switchdev_notifiers(notifier_type, vxlan->dev,
                                 &info.info);
 }
 
 static void vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
-                            struct vxlan_rdst *rd, int type)
+                            struct vxlan_rdst *rd, int type, bool swdev_notify)
 {
-       switch (type) {
-       case RTM_NEWNEIGH:
-               vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, true);
-               break;
-       case RTM_DELNEIGH:
-               vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd, false);
-               break;
+       if (swdev_notify) {
+               switch (type) {
+               case RTM_NEWNEIGH:
+                       vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
+                                                          true);
+                       break;
+               case RTM_DELNEIGH:
+                       vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
+                                                          false);
+                       break;
+               }
        }
 
        __vxlan_fdb_notify(vxlan, fdb, rd, type);
@@ -409,7 +422,7 @@ static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
                .remote_vni = cpu_to_be32(VXLAN_N_VID),
        };
 
-       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH);
+       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true);
 }
 
 static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
@@ -421,7 +434,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
 
        memcpy(f.eth_addr, eth_addr, ETH_ALEN);
 
-       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH);
+       vxlan_fdb_notify(vxlan, &f, &remote, RTM_GETNEIGH, true);
 }
 
 /* Hash Ethernet address */
@@ -531,16 +544,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
        }
 
        rdst = first_remote_rcu(f);
-
-       memset(fdb_info, 0, sizeof(*fdb_info));
-       fdb_info->info.dev = dev;
-       fdb_info->remote_ip = rdst->remote_ip;
-       fdb_info->remote_port = rdst->remote_port;
-       fdb_info->remote_vni = rdst->remote_vni;
-       fdb_info->remote_ifindex = rdst->remote_ifindex;
-       fdb_info->vni = vni;
-       fdb_info->offloaded = rdst->offloaded;
-       ether_addr_copy(fdb_info->eth_addr, mac);
+       vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, fdb_info);
 
 out:
        rcu_read_unlock();
@@ -548,6 +552,75 @@ out:
 }
 EXPORT_SYMBOL_GPL(vxlan_fdb_find_uc);
 
+static int vxlan_fdb_notify_one(struct notifier_block *nb,
+                               const struct vxlan_dev *vxlan,
+                               const struct vxlan_fdb *f,
+                               const struct vxlan_rdst *rdst)
+{
+       struct switchdev_notifier_vxlan_fdb_info fdb_info;
+       int rc;
+
+       vxlan_fdb_switchdev_notifier_info(vxlan, f, rdst, &fdb_info);
+       rc = nb->notifier_call(nb, SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
+                              &fdb_info);
+       return notifier_to_errno(rc);
+}
+
+int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                    struct notifier_block *nb)
+{
+       struct vxlan_dev *vxlan;
+       struct vxlan_rdst *rdst;
+       struct vxlan_fdb *f;
+       unsigned int h;
+       int rc = 0;
+
+       if (!netif_is_vxlan(dev))
+               return -EINVAL;
+       vxlan = netdev_priv(dev);
+
+       spin_lock_bh(&vxlan->hash_lock);
+       for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist) {
+                       if (f->vni == vni) {
+                               list_for_each_entry(rdst, &f->remotes, list) {
+                                       rc = vxlan_fdb_notify_one(nb, vxlan,
+                                                                 f, rdst);
+                                       if (rc)
+                                               goto out;
+                               }
+                       }
+               }
+       }
+
+out:
+       spin_unlock_bh(&vxlan->hash_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(vxlan_fdb_replay);
+
+void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
+{
+       struct vxlan_dev *vxlan;
+       struct vxlan_rdst *rdst;
+       struct vxlan_fdb *f;
+       unsigned int h;
+
+       if (!netif_is_vxlan(dev))
+               return;
+       vxlan = netdev_priv(dev);
+
+       spin_lock_bh(&vxlan->hash_lock);
+       for (h = 0; h < FDB_HASH_SIZE; ++h) {
+               hlist_for_each_entry(f, &vxlan->fdb_head[h], hlist)
+                       if (f->vni == vni)
+                               list_for_each_entry(rdst, &f->remotes, list)
+                                       rdst->offloaded = false;
+       }
+       spin_unlock_bh(&vxlan->hash_lock);
+}
+EXPORT_SYMBOL_GPL(vxlan_fdb_clear_offload);
+
 /* Replace destination of unicast mac */
 static int vxlan_fdb_replace(struct vxlan_fdb *f,
                             union vxlan_addr *ip, __be16 port, __be32 vni,
@@ -700,7 +773,7 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
 
 static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan,
                                         const u8 *mac, __u16 state,
-                                        __be32 src_vni, __u8 ndm_flags)
+                                        __be32 src_vni, __u16 ndm_flags)
 {
        struct vxlan_fdb *f;
 
@@ -720,7 +793,7 @@ static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan,
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                            const u8 *mac, union vxlan_addr *ip,
                            __u16 state, __be16 port, __be32 src_vni,
-                           __be32 vni, __u32 ifindex, __u8 ndm_flags,
+                           __be32 vni, __u32 ifindex, __u16 ndm_flags,
                            struct vxlan_fdb **fdb)
 {
        struct vxlan_rdst *rd = NULL;
@@ -756,9 +829,10 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
                            const u8 *mac, union vxlan_addr *ip,
                            __u16 state, __u16 flags,
                            __be16 port, __be32 src_vni, __be32 vni,
-                           __u32 ifindex, __u8 ndm_flags)
+                           __u32 ifindex, __u16 ndm_flags,
+                           bool swdev_notify)
 {
-       __u8 fdb_flags = (ndm_flags & ~NTF_USE);
+       __u16 fdb_flags = (ndm_flags & ~NTF_USE);
        struct vxlan_rdst *rd = NULL;
        struct vxlan_fdb *f;
        int notify = 0;
@@ -771,16 +845,24 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
                                   "lost race to create %pM\n", mac);
                        return -EEXIST;
                }
-               if (f->state != state) {
-                       f->state = state;
-                       f->updated = jiffies;
-                       notify = 1;
-               }
-               if (f->flags != fdb_flags) {
-                       f->flags = fdb_flags;
-                       f->updated = jiffies;
-                       notify = 1;
+
+               /* Do not allow an externally learned entry to take over an
+                * entry added by the user.
+                */
+               if (!(fdb_flags & NTF_EXT_LEARNED) ||
+                   !(f->flags & NTF_VXLAN_ADDED_BY_USER)) {
+                       if (f->state != state) {
+                               f->state = state;
+                               f->updated = jiffies;
+                               notify = 1;
+                       }
+                       if (f->flags != fdb_flags) {
+                               f->flags = fdb_flags;
+                               f->updated = jiffies;
+                               notify = 1;
+                       }
                }
+
                if ((flags & NLM_F_REPLACE)) {
                        /* Only change unicasts */
                        if (!(is_multicast_ether_addr(f->eth_addr) ||
@@ -822,7 +904,7 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
        if (notify) {
                if (rd == NULL)
                        rd = first_remote_rtnl(f);
-               vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH, swdev_notify);
        }
 
        return 0;
@@ -841,7 +923,7 @@ static void vxlan_fdb_free(struct rcu_head *head)
 }
 
 static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
-                             bool do_notify)
+                             bool do_notify, bool swdev_notify)
 {
        struct vxlan_rdst *rd;
 
@@ -851,7 +933,8 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
        --vxlan->addrcnt;
        if (do_notify)
                list_for_each_entry(rd, &f->remotes, list)
-                       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH);
+                       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH,
+                                        swdev_notify);
 
        hlist_del_rcu(&f->hlist);
        call_rcu(&f->rcu, vxlan_fdb_free);
@@ -866,10 +949,10 @@ static void vxlan_dst_free(struct rcu_head *head)
 }
 
 static void vxlan_fdb_dst_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f,
-                                 struct vxlan_rdst *rd)
+                                 struct vxlan_rdst *rd, bool swdev_notify)
 {
        list_del_rcu(&rd->list);
-       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH);
+       vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH, swdev_notify);
        call_rcu(&rd->rcu, vxlan_dst_free);
 }
 
@@ -968,7 +1051,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
        spin_lock_bh(&vxlan->hash_lock);
        err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
-                              port, src_vni, vni, ifindex, ndm->ndm_flags);
+                              port, src_vni, vni, ifindex,
+                              ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
+                              true);
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -977,7 +1062,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
                              const unsigned char *addr, union vxlan_addr ip,
                              __be16 port, __be32 src_vni, __be32 vni,
-                             u32 ifindex, u16 vid)
+                             u32 ifindex, bool swdev_notify)
 {
        struct vxlan_fdb *f;
        struct vxlan_rdst *rd = NULL;
@@ -997,11 +1082,11 @@ static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
         * otherwise destroy the fdb entry
         */
        if (rd && !list_is_singular(&f->remotes)) {
-               vxlan_fdb_dst_destroy(vxlan, f, rd);
+               vxlan_fdb_dst_destroy(vxlan, f, rd, swdev_notify);
                goto out;
        }
 
-       vxlan_fdb_destroy(vxlan, f, true);
+       vxlan_fdb_destroy(vxlan, f, true, swdev_notify);
 
 out:
        return 0;
@@ -1025,7 +1110,7 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 
        spin_lock_bh(&vxlan->hash_lock);
        err = __vxlan_fdb_delete(vxlan, addr, ip, port, src_vni, vni, ifindex,
-                                vid);
+                                true);
        spin_unlock_bh(&vxlan->hash_lock);
 
        return err;
@@ -1103,7 +1188,7 @@ static bool vxlan_snoop(struct net_device *dev,
 
                rdst->remote_ip = *src_ip;
                f->updated = jiffies;
-               vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH, true);
        } else {
                /* learned new entry */
                spin_lock(&vxlan->hash_lock);
@@ -1116,7 +1201,7 @@ static bool vxlan_snoop(struct net_device *dev,
                                         vxlan->cfg.dst_port,
                                         vni,
                                         vxlan->default_dst.remote_vni,
-                                        ifindex, NTF_SELF);
+                                        ifindex, NTF_SELF, true);
                spin_unlock(&vxlan->hash_lock);
        }
 
@@ -1552,6 +1637,34 @@ drop:
        return 0;
 }
 
+/* Callback from net/ipv{4,6}/udp.c to check that we have a VNI for errors */
+static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
+{
+       struct vxlan_dev *vxlan;
+       struct vxlan_sock *vs;
+       struct vxlanhdr *hdr;
+       __be32 vni;
+
+       if (skb->len < VXLAN_HLEN)
+               return -EINVAL;
+
+       hdr = vxlan_hdr(skb);
+
+       if (!(hdr->vx_flags & VXLAN_HF_VNI))
+               return -EINVAL;
+
+       vs = rcu_dereference_sk_user_data(sk);
+       if (!vs)
+               return -ENOENT;
+
+       vni = vxlan_vni(hdr->vx_vni);
+       vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni);
+       if (!vxlan)
+               return -ENOENT;
+
+       return 0;
+}
+
 static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -2240,6 +2353,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                struct rtable *rt;
                __be16 df = 0;
 
+               if (!ifindex)
+                       ifindex = sock4->sock->sk->sk_bound_dev_if;
+
                rt = vxlan_get_route(vxlan, dev, sock4, skb, ifindex, tos,
                                     dst->sin.sin_addr.s_addr,
                                     &local_ip.sin.sin_addr.s_addr,
@@ -2250,13 +2366,24 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        goto tx_error;
                }
 
-               /* Bypass encapsulation if the destination is local */
                if (!info) {
+                       /* Bypass encapsulation if the destination is local */
                        err = encap_bypass_if_local(skb, dev, vxlan, dst,
                                                    dst_port, ifindex, vni,
                                                    &rt->dst, rt->rt_flags);
                        if (err)
                                goto out_unlock;
+
+                       if (vxlan->cfg.df == VXLAN_DF_SET) {
+                               df = htons(IP_DF);
+                       } else if (vxlan->cfg.df == VXLAN_DF_INHERIT) {
+                               struct ethhdr *eth = eth_hdr(skb);
+
+                               if (ntohs(eth->h_proto) == ETH_P_IPV6 ||
+                                   (ntohs(eth->h_proto) == ETH_P_IP &&
+                                    old_iph->frag_off & htons(IP_DF)))
+                                       df = htons(IP_DF);
+                       }
                } else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) {
                        df = htons(IP_DF);
                }
@@ -2278,6 +2405,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
        } else {
                struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
 
+               if (!ifindex)
+                       ifindex = sock6->sock->sk->sk_bound_dev_if;
+
                ndst = vxlan6_get_route(vxlan, dev, sock6, skb, ifindex, tos,
                                        label, &dst->sin6.sin6_addr,
                                        &local_ip.sin6.sin6_addr,
@@ -2461,7 +2591,7 @@ static void vxlan_cleanup(struct timer_list *t)
                                           "garbage collect %pM\n",
                                           f->eth_addr);
                                f->state = NUD_STALE;
-                               vxlan_fdb_destroy(vxlan, f, true);
+                               vxlan_fdb_destroy(vxlan, f, true, true);
                        } else if (time_before(timeout, next_timer))
                                next_timer = timeout;
                }
@@ -2512,7 +2642,7 @@ static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
        spin_lock_bh(&vxlan->hash_lock);
        f = __vxlan_find_mac(vxlan, all_zeros_mac, vni);
        if (f)
-               vxlan_fdb_destroy(vxlan, f, true);
+               vxlan_fdb_destroy(vxlan, f, true, true);
        spin_unlock_bh(&vxlan->hash_lock);
 }
 
@@ -2566,7 +2696,7 @@ static void vxlan_flush(struct vxlan_dev *vxlan, bool do_all)
                                continue;
                        /* the all_zeros_mac entry is deleted at vxlan_uninit */
                        if (!is_zero_ether_addr(f->eth_addr))
-                               vxlan_fdb_destroy(vxlan, f, true);
+                               vxlan_fdb_destroy(vxlan, f, true, true);
                }
        }
        spin_unlock_bh(&vxlan->hash_lock);
@@ -2809,6 +2939,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
        [IFLA_VXLAN_GPE]        = { .type = NLA_FLAG, },
        [IFLA_VXLAN_REMCSUM_NOPARTIAL]  = { .type = NLA_FLAG },
        [IFLA_VXLAN_TTL_INHERIT]        = { .type = NLA_FLAG },
+       [IFLA_VXLAN_DF]         = { .type = NLA_U8 },
 };
 
 static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -2865,6 +2996,16 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],
                }
        }
 
+       if (data[IFLA_VXLAN_DF]) {
+               enum ifla_vxlan_df df = nla_get_u8(data[IFLA_VXLAN_DF]);
+
+               if (df < 0 || df > VXLAN_DF_MAX) {
+                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_DF],
+                                           "Invalid DF attribute");
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 }
 
@@ -2881,7 +3022,7 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
 };
 
 static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
-                                       __be16 port, u32 flags)
+                                       __be16 port, u32 flags, int ifindex)
 {
        struct socket *sock;
        struct udp_port_cfg udp_conf;
@@ -2899,6 +3040,7 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
        }
 
        udp_conf.local_udp_port = port;
+       udp_conf.bind_ifindex = ifindex;
 
        /* Open UDP socket */
        err = udp_sock_create(net, &udp_conf, &sock);
@@ -2910,7 +3052,8 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
 
 /* Create new listen socket if needed */
 static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
-                                             __be16 port, u32 flags)
+                                             __be16 port, u32 flags,
+                                             int ifindex)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
@@ -2925,7 +3068,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
        for (h = 0; h < VNI_HASH_SIZE; ++h)
                INIT_HLIST_HEAD(&vs->vni_list[h]);
 
-       sock = vxlan_create_sock(net, ipv6, port, flags);
+       sock = vxlan_create_sock(net, ipv6, port, flags, ifindex);
        if (IS_ERR(sock)) {
                kfree(vs);
                return ERR_CAST(sock);
@@ -2948,6 +3091,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
        tunnel_cfg.sk_user_data = vs;
        tunnel_cfg.encap_type = 1;
        tunnel_cfg.encap_rcv = vxlan_rcv;
+       tunnel_cfg.encap_err_lookup = vxlan_err_lookup;
        tunnel_cfg.encap_destroy = NULL;
        tunnel_cfg.gro_receive = vxlan_gro_receive;
        tunnel_cfg.gro_complete = vxlan_gro_complete;
@@ -2962,11 +3106,17 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
        struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs = NULL;
        struct vxlan_dev_node *node;
+       int l3mdev_index = 0;
+
+       if (vxlan->cfg.remote_ifindex)
+               l3mdev_index = l3mdev_master_upper_ifindex_by_index(
+                       vxlan->net, vxlan->cfg.remote_ifindex);
 
        if (!vxlan->cfg.no_share) {
                spin_lock(&vn->sock_lock);
                vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
-                                    vxlan->cfg.dst_port, vxlan->cfg.flags);
+                                    vxlan->cfg.dst_port, vxlan->cfg.flags,
+                                    l3mdev_index);
                if (vs && !refcount_inc_not_zero(&vs->refcnt)) {
                        spin_unlock(&vn->sock_lock);
                        return -EBUSY;
@@ -2975,7 +3125,8 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
        }
        if (!vs)
                vs = vxlan_socket_create(vxlan->net, ipv6,
-                                        vxlan->cfg.dst_port, vxlan->cfg.flags);
+                                        vxlan->cfg.dst_port, vxlan->cfg.flags,
+                                        l3mdev_index);
        if (IS_ERR(vs))
                return PTR_ERR(vs);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -3292,13 +3443,14 @@ static int __vxlan_dev_create(struct net *net, struct net_device *dev,
 
        /* notify default fdb entry */
        if (f)
-               vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH);
+               vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH,
+                                true);
 
        list_add(&vxlan->next, &vn->vxlan_list);
        return 0;
 errout:
        if (f)
-               vxlan_fdb_destroy(vxlan, f, false);
+               vxlan_fdb_destroy(vxlan, f, false, false);
        return err;
 }
 
@@ -3386,11 +3538,8 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
                conf->flags |= VXLAN_F_LEARN;
        }
 
-       if (data[IFLA_VXLAN_AGEING]) {
-               if (changelink)
-                       return -EOPNOTSUPP;
+       if (data[IFLA_VXLAN_AGEING])
                conf->age_interval = nla_get_u32(data[IFLA_VXLAN_AGEING]);
-       }
 
        if (data[IFLA_VXLAN_PROXY]) {
                if (changelink)
@@ -3509,6 +3658,9 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
                conf->mtu = nla_get_u32(tb[IFLA_MTU]);
        }
 
+       if (data[IFLA_VXLAN_DF])
+               conf->df = nla_get_u8(data[IFLA_VXLAN_DF]);
+
        return 0;
 }
 
@@ -3532,6 +3684,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_rdst *dst = &vxlan->default_dst;
+       unsigned long old_age_interval;
        struct vxlan_rdst old_dst;
        struct vxlan_config conf;
        struct vxlan_fdb *f = NULL;
@@ -3542,12 +3695,16 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
        if (err)
                return err;
 
+       old_age_interval = vxlan->cfg.age_interval;
        memcpy(&old_dst, dst, sizeof(struct vxlan_rdst));
 
        err = vxlan_dev_configure(vxlan->net, dev, &conf, true, extack);
        if (err)
                return err;
 
+       if (old_age_interval != vxlan->cfg.age_interval)
+               mod_timer(&vxlan->age_timer, jiffies);
+
        /* handle default dst entry */
        if (!vxlan_addr_equal(&dst->remote_ip, &old_dst.remote_ip)) {
                spin_lock_bh(&vxlan->hash_lock);
@@ -3557,7 +3714,8 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
                                           vxlan->cfg.dst_port,
                                           old_dst.remote_vni,
                                           old_dst.remote_vni,
-                                          old_dst.remote_ifindex, 0);
+                                          old_dst.remote_ifindex,
+                                          true);
 
                if (!vxlan_addr_any(&dst->remote_ip)) {
                        err = vxlan_fdb_create(vxlan, all_zeros_mac,
@@ -3572,7 +3730,8 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
                                spin_unlock_bh(&vxlan->hash_lock);
                                return err;
                        }
-                       vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_NEWNEIGH);
+                       vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f),
+                                        RTM_NEWNEIGH, true);
                }
                spin_unlock_bh(&vxlan->hash_lock);
        }
@@ -3601,6 +3760,7 @@ static size_t vxlan_get_size(const struct net_device *dev)
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_TTL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_TTL_INHERIT */
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_TOS */
+               nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_DF */
                nla_total_size(sizeof(__be32)) + /* IFLA_VXLAN_LABEL */
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_LEARNING */
                nla_total_size(sizeof(__u8)) +  /* IFLA_VXLAN_PROXY */
@@ -3667,6 +3827,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
            nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT,
                       !!(vxlan->cfg.flags & VXLAN_F_TTL_INHERIT)) ||
            nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) ||
+           nla_put_u8(skb, IFLA_VXLAN_DF, vxlan->cfg.df) ||
            nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) ||
            nla_put_u8(skb, IFLA_VXLAN_LEARNING,
                        !!(vxlan->cfg.flags & VXLAN_F_LEARN)) ||
@@ -3749,7 +3910,7 @@ struct net_device *vxlan_dev_create(struct net *net, const char *name,
        memset(&tb, 0, sizeof(tb));
 
        dev = rtnl_create_link(net, name, name_assign_type,
-                              &vxlan_link_ops, tb);
+                              &vxlan_link_ops, tb, NULL);
        if (IS_ERR(dev))
                return dev;
 
@@ -3844,18 +4005,89 @@ out:
        spin_unlock_bh(&vxlan->hash_lock);
 }
 
+static int
+vxlan_fdb_external_learn_add(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       int err;
+
+       spin_lock_bh(&vxlan->hash_lock);
+       err = vxlan_fdb_update(vxlan, fdb_info->eth_addr, &fdb_info->remote_ip,
+                              NUD_REACHABLE,
+                              NLM_F_CREATE | NLM_F_REPLACE,
+                              fdb_info->remote_port,
+                              fdb_info->vni,
+                              fdb_info->remote_vni,
+                              fdb_info->remote_ifindex,
+                              NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
+                              false);
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
+static int
+vxlan_fdb_external_learn_del(struct net_device *dev,
+                            struct switchdev_notifier_vxlan_fdb_info *fdb_info)
+{
+       struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_fdb *f;
+       int err = 0;
+
+       spin_lock_bh(&vxlan->hash_lock);
+
+       f = vxlan_find_mac(vxlan, fdb_info->eth_addr, fdb_info->vni);
+       if (!f)
+               err = -ENOENT;
+       else if (f->flags & NTF_EXT_LEARNED)
+               err = __vxlan_fdb_delete(vxlan, fdb_info->eth_addr,
+                                        fdb_info->remote_ip,
+                                        fdb_info->remote_port,
+                                        fdb_info->vni,
+                                        fdb_info->remote_vni,
+                                        fdb_info->remote_ifindex,
+                                        false);
+
+       spin_unlock_bh(&vxlan->hash_lock);
+
+       return err;
+}
+
 static int vxlan_switchdev_event(struct notifier_block *unused,
                                 unsigned long event, void *ptr)
 {
        struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       struct switchdev_notifier_vxlan_fdb_info *fdb_info;
+       int err = 0;
 
        switch (event) {
        case SWITCHDEV_VXLAN_FDB_OFFLOADED:
                vxlan_fdb_offloaded_set(dev, ptr);
                break;
+       case SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_add(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = true;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
+       case SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE:
+               fdb_info = ptr;
+               err = vxlan_fdb_external_learn_del(dev, fdb_info);
+               if (err) {
+                       err = notifier_from_errno(err);
+                       break;
+               }
+               fdb_info->offloaded = false;
+               vxlan_fdb_offloaded_set(dev, fdb_info);
+               break;
        }
 
-       return 0;
+       return err;
 }
 
 static struct notifier_block vxlan_switchdev_notifier_block __read_mostly = {
index 4d64096..7a42336 100644 (file)
@@ -391,6 +391,7 @@ static netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, struct net_device *dev)
                dev_kfree_skb(skb);
                return -ENOMEM;
        }
+       netdev_sent_queue(dev, skb->len);
        spin_lock_irqsave(&priv->lock, flags);
 
        /* Start from the next BD that should be filled */
@@ -447,6 +448,8 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
 {
        /* Start from the next BD that should be filled */
        struct net_device *dev = priv->ndev;
+       unsigned int bytes_sent = 0;
+       int howmany = 0;
        struct qe_bd *bd;               /* BD pointer */
        u16 bd_status;
        int tx_restart = 0;
@@ -474,6 +477,8 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
                skb = priv->tx_skbuff[priv->skb_dirtytx];
                if (!skb)
                        break;
+               howmany++;
+               bytes_sent += skb->len;
                dev->stats.tx_packets++;
                memset(priv->tx_buffer +
                       (be32_to_cpu(bd->buf) - priv->dma_tx_addr),
@@ -501,6 +506,7 @@ static int hdlc_tx_done(struct ucc_hdlc_private *priv)
        if (tx_restart)
                hdlc_tx_restart(priv);
 
+       netdev_completed_queue(dev, howmany, bytes_sent);
        return 0;
 }
 
@@ -721,6 +727,7 @@ static int uhdlc_open(struct net_device *dev)
                priv->hdlc_busy = 1;
                netif_device_attach(priv->ndev);
                napi_enable(&priv->napi);
+               netdev_reset_queue(dev);
                netif_start_queue(dev);
                hdlc_open(dev);
        }
@@ -812,6 +819,7 @@ static int uhdlc_close(struct net_device *dev)
 
        free_irq(priv->ut_info->uf_info.irq, priv);
        netif_stop_queue(dev);
+       netdev_reset_queue(dev);
        priv->hdlc_busy = 0;
 
        return 0;
index da607fe..22cbe9a 100644 (file)
@@ -561,6 +561,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .hw_ops = &wcn3990_ops,
                .decap_align_bytes = 1,
                .num_peers = TARGET_HL_10_TLV_NUM_PEERS,
+               .n_cipher_suites = 8,
                .ast_skid_limit = TARGET_HL_10_TLV_AST_SKID_LIMIT,
                .num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
                .target_64bit = true,
@@ -594,6 +595,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
        [ATH10K_FW_FEATURE_NO_PS] = "no-ps",
        [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference",
        [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi",
+       [ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL] = "single-chan-info-per-channel",
 };
 
 static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@@ -2183,6 +2185,8 @@ static void ath10k_core_restart(struct work_struct *work)
        if (ret)
                ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d",
                            ret);
+
+       complete(&ar->driver_recovery);
 }
 
 static void ath10k_core_set_coverage_class_work(struct work_struct *work)
@@ -3046,6 +3050,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
        init_completion(&ar->scan.completed);
        init_completion(&ar->scan.on_channel);
        init_completion(&ar->target_suspend);
+       init_completion(&ar->driver_recovery);
        init_completion(&ar->wow.wakeup_completed);
 
        init_completion(&ar->install_key_done);
index 0424180..d14a4f9 100644 (file)
@@ -474,6 +474,7 @@ struct ath10k_htt_data_stats {
        u64 bw[ATH10K_COUNTER_TYPE_MAX][ATH10K_BW_NUM];
        u64 nss[ATH10K_COUNTER_TYPE_MAX][ATH10K_NSS_NUM];
        u64 gi[ATH10K_COUNTER_TYPE_MAX][ATH10K_GI_NUM];
+       u64 rate_table[ATH10K_COUNTER_TYPE_MAX][ATH10K_RATE_TABLE_NUM];
 };
 
 struct ath10k_htt_tx_stats {
@@ -760,6 +761,9 @@ enum ath10k_fw_features {
        /* Firmware load is done externally, not by bmi */
        ATH10K_FW_FEATURE_NON_BMI = 19,
 
+       /* Firmware sends only one chan_info event per channel */
+       ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL = 20,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
@@ -960,6 +964,7 @@ struct ath10k {
        } hif;
 
        struct completion target_suspend;
+       struct completion driver_recovery;
 
        const struct ath10k_hw_regs *regs;
        const struct ath10k_hw_ce_regs *hw_ce_regs;
index 4d28063..eadae2f 100644 (file)
@@ -867,9 +867,105 @@ static const struct ath10k_mem_region qca9984_hw10_mem_regions[] = {
        },
 };
 
+static const struct ath10k_mem_section ipq4019_soc_reg_range[] = {
+       {0x080000, 0x080004},
+       {0x080020, 0x080024},
+       {0x080028, 0x080050},
+       {0x0800d4, 0x0800ec},
+       {0x08010c, 0x080118},
+       {0x080284, 0x080290},
+       {0x0802a8, 0x0802b8},
+       {0x0802dc, 0x08030c},
+       {0x082000, 0x083fff}
+};
+
+static const struct ath10k_mem_region qca4019_hw10_mem_regions[] = {
+       {
+               .type = ATH10K_MEM_REGION_TYPE_DRAM,
+               .start = 0x400000,
+               .len = 0x68000,
+               .name = "DRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0xC0000,
+               .len = 0x40000,
+               .name = "SRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0x98000,
+               .len = 0x50000,
+               .name = "IRAM",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x30000,
+               .len = 0x7000,
+               .name = "APB REG 1",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x3f000,
+               .len = 0x3000,
+               .name = "APB REG 2",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x43000,
+               .len = 0x3000,
+               .name = "WIFI REG",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_IOREG,
+               .start = 0x4A000,
+               .len = 0x5000,
+               .name = "CE REG",
+               .section_table = {
+                       .sections = NULL,
+                       .size = 0,
+               },
+       },
+       {
+               .type = ATH10K_MEM_REGION_TYPE_REG,
+               .start = 0x080000,
+               .len = 0x083fff - 0x080000,
+               .name = "REG_TOTAL",
+               .section_table = {
+                       .sections = ipq4019_soc_reg_range,
+                       .size = ARRAY_SIZE(ipq4019_soc_reg_range),
+               },
+       },
+};
+
 static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        {
                .hw_id = QCA6174_HW_1_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -877,6 +973,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_1_1_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -884,6 +981,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_1_3_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw10_mem_regions),
@@ -891,6 +989,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_2_1_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw21_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw21_mem_regions),
@@ -898,6 +997,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_3_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -905,6 +1005,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA6174_HW_3_2_VERSION,
+               .hw_rev = ATH10K_HW_QCA6174,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -912,6 +1013,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9377_HW_1_1_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9377,
                .region_table = {
                        .regions = qca6174_hw30_mem_regions,
                        .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
@@ -919,6 +1021,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA988X_HW_2_0_VERSION,
+               .hw_rev = ATH10K_HW_QCA988X,
                .region_table = {
                        .regions = qca988x_hw20_mem_regions,
                        .size = ARRAY_SIZE(qca988x_hw20_mem_regions),
@@ -926,6 +1029,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9984_HW_1_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9984,
                .region_table = {
                        .regions = qca9984_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca9984_hw10_mem_regions),
@@ -933,6 +1037,7 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA9888_HW_2_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA9888,
                .region_table = {
                        .regions = qca9984_hw10_mem_regions,
                        .size = ARRAY_SIZE(qca9984_hw10_mem_regions),
@@ -940,12 +1045,20 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
        },
        {
                .hw_id = QCA99X0_HW_2_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA99X0,
                .region_table = {
                        .regions = qca99x0_hw20_mem_regions,
                        .size = ARRAY_SIZE(qca99x0_hw20_mem_regions),
                },
        },
-
+       {
+               .hw_id = QCA4019_HW_1_0_DEV_VERSION,
+               .hw_rev = ATH10K_HW_QCA4019,
+               .region_table = {
+                       .regions = qca4019_hw10_mem_regions,
+                       .size = ARRAY_SIZE(qca4019_hw10_mem_regions),
+               },
+       },
 };
 
 static u32 ath10k_coredump_get_ramdump_size(struct ath10k *ar)
@@ -987,7 +1100,8 @@ const struct ath10k_hw_mem_layout *ath10k_coredump_get_mem_layout(struct ath10k
                return NULL;
 
        for (i = 0; i < ARRAY_SIZE(hw_mem_layouts); i++) {
-               if (ar->target_version == hw_mem_layouts[i].hw_id)
+               if (ar->target_version == hw_mem_layouts[i].hw_id &&
+                   ar->hw_rev == hw_mem_layouts[i].hw_rev)
                        return &hw_mem_layouts[i];
        }
 
index 3baaf9d..5dac653 100644 (file)
@@ -165,6 +165,7 @@ struct ath10k_mem_region {
  */
 struct ath10k_hw_mem_layout {
        u32 hw_id;
+       u32 hw_rev;
 
        struct {
                const struct ath10k_mem_region *regions;
index b09cdc6..0f3fd65 100644 (file)
@@ -665,7 +665,7 @@ static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file,
                                                       "retry", "ampdu"};
        const char *str[ATH10K_COUNTER_TYPE_MAX] = {"bytes", "packets"};
        int len = 0, i, j, k, retval = 0;
-       const int size = 2 * 4096;
+       const int size = 16 * 4096;
        char *buf;
 
        buf = kzalloc(size, GFP_KERNEL);
@@ -719,6 +719,16 @@ static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file,
                                len += scnprintf(buf + len, size - len, "%llu ",
                                                 stats->legacy[j][i]);
                        len += scnprintf(buf + len, size - len, "\n");
+                       len += scnprintf(buf + len, size - len,
+                                        " Rate table %s (1,2 ... Mbps)\n  ",
+                                        str[j]);
+                       for (i = 0; i < ATH10K_RATE_TABLE_NUM; i++) {
+                               len += scnprintf(buf + len, size - len, "%llu ",
+                                                stats->rate_table[j][i]);
+                               if (!((i + 1) % 8))
+                                       len +=
+                                       scnprintf(buf + len, size - len, "\n  ");
+                       }
                }
        }
 
index ffec98f..984b045 100644 (file)
@@ -2650,7 +2650,7 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
 {
        struct rate_info *txrate = &arsta->txrate;
        struct ath10k_htt_tx_stats *tx_stats;
-       int ht_idx, gi, mcs, bw, nss;
+       int idx, ht_idx, gi, mcs, bw, nss;
 
        if (!arsta->tx_stats)
                return;
@@ -2661,6 +2661,8 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
        mcs = txrate->mcs;
        bw = txrate->bw;
        nss = txrate->nss;
+       idx = mcs * 8 + 8 * 10 * nss;
+       idx += bw * 2 + gi;
 
 #define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name]
 
@@ -2709,12 +2711,16 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
                        pstats->succ_bytes + pstats->retry_bytes;
                STATS_OP_FMT(AMPDU).gi[0][gi] +=
                        pstats->succ_bytes + pstats->retry_bytes;
+               STATS_OP_FMT(AMPDU).rate_table[0][idx] +=
+                       pstats->succ_bytes + pstats->retry_bytes;
                STATS_OP_FMT(AMPDU).bw[1][bw] +=
                        pstats->succ_pkts + pstats->retry_pkts;
                STATS_OP_FMT(AMPDU).nss[1][nss] +=
                        pstats->succ_pkts + pstats->retry_pkts;
                STATS_OP_FMT(AMPDU).gi[1][gi] +=
                        pstats->succ_pkts + pstats->retry_pkts;
+               STATS_OP_FMT(AMPDU).rate_table[1][idx] +=
+                       pstats->succ_pkts + pstats->retry_pkts;
        } else {
                tx_stats->ack_fails +=
                                ATH10K_HW_BA_FAIL(pstats->flags);
@@ -2743,6 +2749,15 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
        STATS_OP_FMT(RETRY).bw[1][bw] += pstats->retry_pkts;
        STATS_OP_FMT(RETRY).nss[1][nss] += pstats->retry_pkts;
        STATS_OP_FMT(RETRY).gi[1][gi] += pstats->retry_pkts;
+
+       if (txrate->flags >= RATE_INFO_FLAGS_MCS) {
+               STATS_OP_FMT(SUCC).rate_table[0][idx] += pstats->succ_bytes;
+               STATS_OP_FMT(SUCC).rate_table[1][idx] += pstats->succ_pkts;
+               STATS_OP_FMT(FAIL).rate_table[0][idx] += pstats->failed_bytes;
+               STATS_OP_FMT(FAIL).rate_table[1][idx] += pstats->failed_pkts;
+               STATS_OP_FMT(RETRY).rate_table[0][idx] += pstats->retry_bytes;
+               STATS_OP_FMT(RETRY).rate_table[1][idx] += pstats->retry_pkts;
+       }
 }
 
 static void
index 7e49342..1db2a30 100644 (file)
@@ -6296,8 +6296,10 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
                        arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats),
                                                  GFP_KERNEL);
-                       if (!arsta->tx_stats)
+                       if (!arsta->tx_stats) {
+                               ret = -ENOMEM;
                                goto exit;
+                       }
                }
 
                num_tdls_stations = ath10k_mac_tdls_vif_stations_count(hw, vif);
@@ -6385,8 +6387,10 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           "mac vdev %d peer delete %pM sta %pK (sta gone)\n",
                           arvif->vdev_id, sta->addr, sta);
 
-               if (ath10k_debug_is_extd_tx_stats_enabled(ar))
+               if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
                        kfree(arsta->tx_stats);
+                       arsta->tx_stats = NULL;
+               }
 
                if (sta->tdls) {
                        ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
@@ -8313,7 +8317,6 @@ static u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd)
 
 static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
 {
-       struct pci_dev __maybe_unused *pdev = to_pci_dev(ar->dev);
        acpi_handle root_handle;
        acpi_handle handle;
        struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
@@ -8321,7 +8324,7 @@ static int ath10k_mac_get_wrdd_regulatory(struct ath10k *ar, u16 *rd)
        u32 alpha2_code;
        char alpha2[3];
 
-       root_handle = ACPI_HANDLE(&pdev->dev);
+       root_handle = ACPI_HANDLE(ar->dev);
        if (!root_handle)
                return -EOPNOTSUPP;
 
index 56cb183..f0429be 100644 (file)
@@ -543,7 +543,7 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
                goto out;
 
        if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
-               ath10k_err(ar, "capablity req rejected: %d\n", resp->resp.error);
+               ath10k_err(ar, "capability req rejected: %d\n", resp->resp.error);
                ret = -EINVAL;
                goto out;
        }
@@ -623,7 +623,7 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
                goto out;
        }
 
-       ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capablity request completed\n");
+       ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi host capability request completed\n");
        return 0;
 
 out:
@@ -657,7 +657,7 @@ ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi)
                               wlfw_ind_register_req_msg_v01_ei, &req);
        if (ret < 0) {
                qmi_txn_cancel(&txn);
-               ath10k_err(ar, "failed to send indication registed request: %d\n", ret);
+               ath10k_err(ar, "failed to send indication registered request: %d\n", ret);
                goto out;
        }
 
@@ -931,7 +931,7 @@ static int ath10k_qmi_setup_msa_resources(struct ath10k_qmi *qmi, u32 msa_size)
                qmi->msa_mem_size = resource_size(&r);
                qmi->msa_va = devm_memremap(dev, qmi->msa_pa, qmi->msa_mem_size,
                                            MEMREMAP_WT);
-               if (!qmi->msa_pa) {
+               if (!qmi->msa_va) {
                        dev_err(dev, "failed to map memory region: %pa\n", &r.start);
                        return -EBUSY;
                }
index 8d3d9bc..dad7e1c 100644 (file)
@@ -46,14 +46,14 @@ static char *const ce_name[] = {
        "WLAN_CE_11",
 };
 
-static struct ath10k_wcn3990_vreg_info vreg_cfg[] = {
-       {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
-       {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
-       {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
-       {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
+static struct ath10k_vreg_info vreg_cfg[] = {
+       {NULL, "vdd-0.8-cx-mx", 800000, 850000, 0, 0, false},
+       {NULL, "vdd-1.8-xo", 1800000, 1850000, 0, 0, false},
+       {NULL, "vdd-1.3-rfa", 1300000, 1350000, 0, 0, false},
+       {NULL, "vdd-3.3-ch0", 3300000, 3350000, 0, 0, false},
 };
 
-static struct ath10k_wcn3990_clk_info clk_cfg[] = {
+static struct ath10k_clk_info clk_cfg[] = {
        {NULL, "cxo_ref_clk_pin", 0, false},
 };
 
@@ -474,14 +474,14 @@ static struct service_to_pipe target_service_to_ce_map_wlan[] = {
        },
 };
 
-void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
+static void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
 
        iowrite32(value, ar_snoc->mem + offset);
 }
 
-u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
+static u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        u32 val;
@@ -918,7 +918,9 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar)
 
 static void ath10k_snoc_hif_stop(struct ath10k *ar)
 {
-       ath10k_snoc_irq_disable(ar);
+       if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+               ath10k_snoc_irq_disable(ar);
+
        napi_synchronize(&ar->napi);
        napi_disable(&ar->napi);
        ath10k_snoc_buffer_cleanup(ar);
@@ -927,10 +929,14 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
 
 static int ath10k_snoc_hif_start(struct ath10k *ar)
 {
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
        napi_enable(&ar->napi);
        ath10k_snoc_irq_enable(ar);
        ath10k_snoc_rx_post(ar);
 
+       clear_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags);
+
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
 
        return 0;
@@ -994,7 +1000,8 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar)
 
 static void ath10k_snoc_wlan_disable(struct ath10k *ar)
 {
-       ath10k_qmi_wlan_disable(ar);
+       if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+               ath10k_qmi_wlan_disable(ar);
 }
 
 static void ath10k_snoc_hif_power_down(struct ath10k *ar)
@@ -1091,6 +1098,11 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
        struct ath10k *ar = container_of(ctx, struct ath10k, napi);
        int done = 0;
 
+       if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+               napi_complete(ctx);
+               return done;
+       }
+
        ath10k_ce_per_engine_service_any(ar);
        done = ath10k_htt_txrx_compl_task(ar, budget);
 
@@ -1187,17 +1199,29 @@ int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type)
        struct ath10k_bus_params bus_params;
        int ret;
 
+       if (test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags))
+               return 0;
+
        switch (type) {
        case ATH10K_QMI_EVENT_FW_READY_IND:
+               if (test_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags)) {
+                       queue_work(ar->workqueue, &ar->restart_work);
+                       break;
+               }
+
                bus_params.dev_type = ATH10K_DEV_TYPE_LL;
                bus_params.chip_id = ar_snoc->target_info.soc_version;
                ret = ath10k_core_register(ar, &bus_params);
                if (ret) {
-                       ath10k_err(ar, "failed to register driver core: %d\n",
+                       ath10k_err(ar, "Failed to register driver core: %d\n",
                                   ret);
+                       return ret;
                }
+               set_bit(ATH10K_SNOC_FLAG_REGISTERED, &ar_snoc->flags);
                break;
        case ATH10K_QMI_EVENT_FW_DOWN_IND:
+               set_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags);
+               set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
                break;
        default:
                ath10k_err(ar, "invalid fw indication: %llx\n", type);
@@ -1246,7 +1270,7 @@ static void ath10k_snoc_release_resource(struct ath10k *ar)
 }
 
 static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev,
-                               struct ath10k_wcn3990_vreg_info *vreg_info)
+                               struct ath10k_vreg_info *vreg_info)
 {
        struct regulator *reg;
        int ret = 0;
@@ -1284,7 +1308,7 @@ done:
 }
 
 static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
-                              struct ath10k_wcn3990_clk_info *clk_info)
+                              struct ath10k_clk_info *clk_info)
 {
        struct clk *handle;
        int ret = 0;
@@ -1311,10 +1335,80 @@ static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
        return ret;
 }
 
-static int ath10k_wcn3990_vreg_on(struct ath10k *ar)
+static int __ath10k_snoc_vreg_on(struct ath10k *ar,
+                                struct ath10k_vreg_info *vreg_info)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
+                  vreg_info->name);
+
+       ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
+                                   vreg_info->max_v);
+       if (ret) {
+               ath10k_err(ar,
+                          "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
+                          vreg_info->name, vreg_info->min_v, vreg_info->max_v);
+               return ret;
+       }
+
+       if (vreg_info->load_ua) {
+               ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua);
+               if (ret < 0) {
+                       ath10k_err(ar, "failed to set regulator %s load: %d\n",
+                                  vreg_info->name, vreg_info->load_ua);
+                       goto err_set_load;
+               }
+       }
+
+       ret = regulator_enable(vreg_info->reg);
+       if (ret) {
+               ath10k_err(ar, "failed to enable regulator %s\n",
+                          vreg_info->name);
+               goto err_enable;
+       }
+
+       if (vreg_info->settle_delay)
+               udelay(vreg_info->settle_delay);
+
+       return 0;
+
+err_enable:
+       regulator_set_load(vreg_info->reg, 0);
+err_set_load:
+       regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+
+       return ret;
+}
+
+static int __ath10k_snoc_vreg_off(struct ath10k *ar,
+                                 struct ath10k_vreg_info *vreg_info)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
+                  vreg_info->name);
+
+       ret = regulator_disable(vreg_info->reg);
+       if (ret)
+               ath10k_err(ar, "failed to disable regulator %s\n",
+                          vreg_info->name);
+
+       ret = regulator_set_load(vreg_info->reg, 0);
+       if (ret < 0)
+               ath10k_err(ar, "failed to set load %s\n", vreg_info->name);
+
+       ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+       if (ret)
+               ath10k_err(ar, "failed to set voltage %s\n", vreg_info->name);
+
+       return ret;
+}
+
+static int ath10k_snoc_vreg_on(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_vreg_info *vreg_info;
+       struct ath10k_vreg_info *vreg_info;
        int ret = 0;
        int i;
 
@@ -1324,62 +1418,30 @@ static int ath10k_wcn3990_vreg_on(struct ath10k *ar)
                if (!vreg_info->reg)
                        continue;
 
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
-                          vreg_info->name);
-
-               ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
-                                           vreg_info->max_v);
-               if (ret) {
-                       ath10k_err(ar,
-                                  "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
-                                  vreg_info->name, vreg_info->min_v, vreg_info->max_v);
-                       goto err_reg_config;
-               }
-
-               if (vreg_info->load_ua) {
-                       ret = regulator_set_load(vreg_info->reg,
-                                                vreg_info->load_ua);
-                       if (ret < 0) {
-                               ath10k_err(ar,
-                                          "failed to set regulator %s load: %d\n",
-                                          vreg_info->name,
-                                          vreg_info->load_ua);
-                               goto err_reg_config;
-                       }
-               }
-
-               ret = regulator_enable(vreg_info->reg);
-               if (ret) {
-                       ath10k_err(ar, "failed to enable regulator %s\n",
-                                  vreg_info->name);
+               ret = __ath10k_snoc_vreg_on(ar, vreg_info);
+               if (ret)
                        goto err_reg_config;
-               }
-
-               if (vreg_info->settle_delay)
-                       udelay(vreg_info->settle_delay);
        }
 
        return 0;
 
 err_reg_config:
-       for (; i >= 0; i--) {
+       for (i = i - 1; i >= 0; i--) {
                vreg_info = &ar_snoc->vreg[i];
 
                if (!vreg_info->reg)
                        continue;
 
-               regulator_disable(vreg_info->reg);
-               regulator_set_load(vreg_info->reg, 0);
-               regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+               __ath10k_snoc_vreg_off(ar, vreg_info);
        }
 
        return ret;
 }
 
-static int ath10k_wcn3990_vreg_off(struct ath10k *ar)
+static int ath10k_snoc_vreg_off(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_vreg_info *vreg_info;
+       struct ath10k_vreg_info *vreg_info;
        int ret = 0;
        int i;
 
@@ -1389,33 +1451,16 @@ static int ath10k_wcn3990_vreg_off(struct ath10k *ar)
                if (!vreg_info->reg)
                        continue;
 
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
-                          vreg_info->name);
-
-               ret = regulator_disable(vreg_info->reg);
-               if (ret)
-                       ath10k_err(ar, "failed to disable regulator %s\n",
-                                  vreg_info->name);
-
-               ret = regulator_set_load(vreg_info->reg, 0);
-               if (ret < 0)
-                       ath10k_err(ar, "failed to set load %s\n",
-                                  vreg_info->name);
-
-               ret = regulator_set_voltage(vreg_info->reg, 0,
-                                           vreg_info->max_v);
-               if (ret)
-                       ath10k_err(ar, "failed to set voltage %s\n",
-                                  vreg_info->name);
+               ret = __ath10k_snoc_vreg_off(ar, vreg_info);
        }
 
        return ret;
 }
 
-static int ath10k_wcn3990_clk_init(struct ath10k *ar)
+static int ath10k_snoc_clk_init(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_clk_info *clk_info;
+       struct ath10k_clk_info *clk_info;
        int ret = 0;
        int i;
 
@@ -1449,7 +1494,7 @@ static int ath10k_wcn3990_clk_init(struct ath10k *ar)
        return 0;
 
 err_clock_config:
-       for (; i >= 0; i--) {
+       for (i = i - 1; i >= 0; i--) {
                clk_info = &ar_snoc->clk[i];
 
                if (!clk_info->handle)
@@ -1461,10 +1506,10 @@ err_clock_config:
        return ret;
 }
 
-static int ath10k_wcn3990_clk_deinit(struct ath10k *ar)
+static int ath10k_snoc_clk_deinit(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_wcn3990_clk_info *clk_info;
+       struct ath10k_clk_info *clk_info;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
@@ -1488,18 +1533,18 @@ static int ath10k_hw_power_on(struct ath10k *ar)
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
 
-       ret = ath10k_wcn3990_vreg_on(ar);
+       ret = ath10k_snoc_vreg_on(ar);
        if (ret)
                return ret;
 
-       ret = ath10k_wcn3990_clk_init(ar);
+       ret = ath10k_snoc_clk_init(ar);
        if (ret)
                goto vreg_off;
 
        return ret;
 
 vreg_off:
-       ath10k_wcn3990_vreg_off(ar);
+       ath10k_snoc_vreg_off(ar);
        return ret;
 }
 
@@ -1509,9 +1554,9 @@ static int ath10k_hw_power_off(struct ath10k *ar)
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
 
-       ath10k_wcn3990_clk_deinit(ar);
+       ath10k_snoc_clk_deinit(ar);
 
-       ret = ath10k_wcn3990_vreg_off(ar);
+       ret = ath10k_snoc_vreg_off(ar);
 
        return ret;
 }
@@ -1628,8 +1673,17 @@ err_core_destroy:
 static int ath10k_snoc_remove(struct platform_device *pdev)
 {
        struct ath10k *ar = platform_get_drvdata(pdev);
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
 
        ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n");
+
+       reinit_completion(&ar->driver_recovery);
+
+       if (test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags))
+               wait_for_completion_timeout(&ar->driver_recovery, 3 * HZ);
+
+       set_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags);
+
        ath10k_core_unregister(ar);
        ath10k_hw_power_off(ar);
        ath10k_snoc_free_irq(ar);
@@ -1641,12 +1695,12 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver ath10k_snoc_driver = {
-               .probe  = ath10k_snoc_probe,
-               .remove = ath10k_snoc_remove,
-               .driver = {
-                       .name   = "ath10k_snoc",
-                       .of_match_table = ath10k_snoc_dt_match,
-               },
+       .probe  = ath10k_snoc_probe,
+       .remove = ath10k_snoc_remove,
+       .driver = {
+               .name   = "ath10k_snoc",
+               .of_match_table = ath10k_snoc_dt_match,
+       },
 };
 module_platform_driver(ath10k_snoc_driver);
 
index e1d2d66..2b2f23c 100644 (file)
@@ -53,7 +53,7 @@ struct ath10k_snoc_ce_irq {
        u32 irq_line;
 };
 
-struct ath10k_wcn3990_vreg_info {
+struct ath10k_vreg_info {
        struct regulator *reg;
        const char *name;
        u32 min_v;
@@ -63,13 +63,19 @@ struct ath10k_wcn3990_vreg_info {
        bool required;
 };
 
-struct ath10k_wcn3990_clk_info {
+struct ath10k_clk_info {
        struct clk *handle;
        const char *name;
        u32 freq;
        bool required;
 };
 
+enum ath10k_snoc_flags {
+       ATH10K_SNOC_FLAG_REGISTERED,
+       ATH10K_SNOC_FLAG_UNREGISTERING,
+       ATH10K_SNOC_FLAG_RECOVERY,
+};
+
 struct ath10k_snoc {
        struct platform_device *dev;
        struct ath10k *ar;
@@ -81,9 +87,10 @@ struct ath10k_snoc {
        struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX];
        struct ath10k_ce ce;
        struct timer_list rx_post_retry;
-       struct ath10k_wcn3990_vreg_info *vreg;
-       struct ath10k_wcn3990_clk_info *clk;
+       struct ath10k_vreg_info *vreg;
+       struct ath10k_clk_info *clk;
        struct ath10k_qmi *qmi;
+       unsigned long int flags;
 };
 
 static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
@@ -91,8 +98,6 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
        return (struct ath10k_snoc *)ar->drv_priv;
 }
 
-void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value);
-u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset);
 int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type);
 
 #endif /* _SNOC_H_ */
index bab8b25..ad4114a 100644 (file)
@@ -762,6 +762,9 @@ static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar,
        arg->noise_floor = ev->noise_floor;
        arg->rx_clear_count = ev->rx_clear_count;
        arg->cycle_count = ev->cycle_count;
+       if (test_bit(ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL,
+                    ar->running_fw->fw_file.fw_features))
+               arg->mac_clk_mhz = ev->mac_clk_mhz;
 
        kfree(tb);
        return 0;
@@ -3452,7 +3455,6 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar,
        struct wmi_tlv *tlv;
        struct sk_buff *skb;
        __le32 *channel_list;
-       u16 tlv_len;
        size_t len;
        void *ptr;
        u32 i;
@@ -3510,8 +3512,6 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar,
        /* nlo_configured_parameters(nlo_list) */
        cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count,
                                               WMI_NLO_MAX_SSIDS));
-       tlv_len = __le32_to_cpu(cmd->no_of_ssids) *
-               sizeof(struct nlo_configured_parameters);
 
        tlv = ptr;
        tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
index 92c25f5..bf8a432 100644 (file)
@@ -1579,6 +1579,16 @@ struct ath10k_mgmt_tx_pkt_addr {
        dma_addr_t paddr;
 };
 
+struct chan_info_params {
+       u32 err_code;
+       u32 freq;
+       u32 cmd_flags;
+       u32 noise_floor;
+       u32 rx_clear_count;
+       u32 cycle_count;
+       u32 mac_clk_mhz;
+};
+
 struct wmi_tlv_mgmt_tx_compl_ev {
        __le32 desc_id;
        __le32 status;
index 25e8fa7..659513b 100644 (file)
@@ -2554,60 +2554,69 @@ static int ath10k_wmi_10_4_op_pull_ch_info_ev(struct ath10k *ar,
        return 0;
 }
 
-void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+/*
+ * Handle the channel info event for firmware which only sends one
+ * chan_info event per scanned channel.
+ */
+static void ath10k_wmi_event_chan_info_unpaired(struct ath10k *ar,
+                                               struct chan_info_params *params)
 {
-       struct wmi_ch_info_ev_arg arg = {};
        struct survey_info *survey;
-       u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
-       int idx, ret;
+       int idx;
 
-       ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
-       if (ret) {
-               ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
+       if (params->cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+               ath10k_dbg(ar, ATH10K_DBG_WMI, "chan info report completed\n");
                return;
        }
 
-       err_code = __le32_to_cpu(arg.err_code);
-       freq = __le32_to_cpu(arg.freq);
-       cmd_flags = __le32_to_cpu(arg.cmd_flags);
-       noise_floor = __le32_to_cpu(arg.noise_floor);
-       rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
-       cycle_count = __le32_to_cpu(arg.cycle_count);
+       idx = freq_to_idx(ar, params->freq);
+       if (idx >= ARRAY_SIZE(ar->survey)) {
+               ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n",
+                           params->freq, idx);
+               return;
+       }
 
-       ath10k_dbg(ar, ATH10K_DBG_WMI,
-                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
-                  err_code, freq, cmd_flags, noise_floor, rx_clear_count,
-                  cycle_count);
+       survey = &ar->survey[idx];
 
-       spin_lock_bh(&ar->data_lock);
+       if (!params->mac_clk_mhz || !survey)
+               return;
 
-       switch (ar->scan.state) {
-       case ATH10K_SCAN_IDLE:
-       case ATH10K_SCAN_STARTING:
-               ath10k_warn(ar, "received chan info event without a scan request, ignoring\n");
-               goto exit;
-       case ATH10K_SCAN_RUNNING:
-       case ATH10K_SCAN_ABORTING:
-               break;
-       }
+       memset(survey, 0, sizeof(*survey));
 
-       idx = freq_to_idx(ar, freq);
+       survey->noise = params->noise_floor;
+       survey->time = (params->cycle_count / params->mac_clk_mhz) / 1000;
+       survey->time_busy = (params->rx_clear_count / params->mac_clk_mhz) / 1000;
+       survey->filled |= SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
+                         SURVEY_INFO_TIME_BUSY;
+}
+
+/*
+ * Handle the channel info event for firmware which sends chan_info
+ * event in pairs(start and stop events) for every scanned channel.
+ */
+static void ath10k_wmi_event_chan_info_paired(struct ath10k *ar,
+                                             struct chan_info_params *params)
+{
+       struct survey_info *survey;
+       int idx;
+
+       idx = freq_to_idx(ar, params->freq);
        if (idx >= ARRAY_SIZE(ar->survey)) {
                ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n",
-                           freq, idx);
-               goto exit;
+                           params->freq, idx);
+               return;
        }
 
-       if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+       if (params->cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
                if (ar->ch_info_can_report_survey) {
                        survey = &ar->survey[idx];
-                       survey->noise = noise_floor;
+                       survey->noise = params->noise_floor;
                        survey->filled = SURVEY_INFO_NOISE_DBM;
 
                        ath10k_hw_fill_survey_time(ar,
                                                   survey,
-                                                  cycle_count,
-                                                  rx_clear_count,
+                                                  params->cycle_count,
+                                                  params->rx_clear_count,
                                                   ar->survey_last_cycle_count,
                                                   ar->survey_last_rx_clear_count);
                }
@@ -2617,11 +2626,56 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
                ar->ch_info_can_report_survey = true;
        }
 
-       if (!(cmd_flags & WMI_CHAN_INFO_FLAG_PRE_COMPLETE)) {
-               ar->survey_last_rx_clear_count = rx_clear_count;
-               ar->survey_last_cycle_count = cycle_count;
+       if (!(params->cmd_flags & WMI_CHAN_INFO_FLAG_PRE_COMPLETE)) {
+               ar->survey_last_rx_clear_count = params->rx_clear_count;
+               ar->survey_last_cycle_count = params->cycle_count;
+       }
+}
+
+void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct chan_info_params ch_info_param;
+       struct wmi_ch_info_ev_arg arg = {};
+       int ret;
+
+       ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
+       if (ret) {
+               ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
+               return;
+       }
+
+       ch_info_param.err_code = __le32_to_cpu(arg.err_code);
+       ch_info_param.freq = __le32_to_cpu(arg.freq);
+       ch_info_param.cmd_flags = __le32_to_cpu(arg.cmd_flags);
+       ch_info_param.noise_floor = __le32_to_cpu(arg.noise_floor);
+       ch_info_param.rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
+       ch_info_param.cycle_count = __le32_to_cpu(arg.cycle_count);
+       ch_info_param.mac_clk_mhz = __le32_to_cpu(arg.mac_clk_mhz);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+                  ch_info_param.err_code, ch_info_param.freq, ch_info_param.cmd_flags,
+                  ch_info_param.noise_floor, ch_info_param.rx_clear_count,
+                  ch_info_param.cycle_count);
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (ar->scan.state) {
+       case ATH10K_SCAN_IDLE:
+       case ATH10K_SCAN_STARTING:
+               ath10k_warn(ar, "received chan info event without a scan request, ignoring\n");
+               goto exit;
+       case ATH10K_SCAN_RUNNING:
+       case ATH10K_SCAN_ABORTING:
+               break;
        }
 
+       if (test_bit(ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL,
+                    ar->running_fw->fw_file.fw_features))
+               ath10k_wmi_event_chan_info_unpaired(ar, &ch_info_param);
+       else
+               ath10k_wmi_event_chan_info_paired(ar, &ch_info_param);
+
 exit:
        spin_unlock_bh(&ar->data_lock);
 }
index f7badd0..58e33ab 100644 (file)
@@ -4991,6 +4991,7 @@ enum wmi_rate_preamble {
 #define ATH10K_LEGACY_NUM      12
 #define ATH10K_GI_NUM          2
 #define ATH10K_HT_MCS_NUM      32
+#define ATH10K_RATE_TABLE_NUM  320
 
 /* Value to disable fixed rate setting */
 #define WMI_FIXED_RATE_NONE    (0xff)
@@ -6441,6 +6442,14 @@ struct wmi_chan_info_event {
        __le32 noise_floor;
        __le32 rx_clear_count;
        __le32 cycle_count;
+       __le32 chan_tx_pwr_range;
+       __le32 chan_tx_pwr_tp;
+       __le32 rx_frame_count;
+       __le32 my_bss_rx_cycle_count;
+       __le32 rx_11b_mode_data_duration;
+       __le32 tx_frame_cnt;
+       __le32 mac_clk_mhz;
+
 } __packed;
 
 struct wmi_10_4_chan_info_event {
@@ -6669,6 +6678,10 @@ struct wmi_ch_info_ev_arg {
        __le32 chan_tx_pwr_range;
        __le32 chan_tx_pwr_tp;
        __le32 rx_frame_count;
+       __le32 my_bss_rx_cycle_count;
+       __le32 rx_11b_mode_data_duration;
+       __le32 tx_frame_cnt;
+       __le32 mac_clk_mhz;
 };
 
 /* From 10.4 firmware, not sure all have the same values. */
index 51b26b3..36d4245 100644 (file)
@@ -135,7 +135,7 @@ static void ath10k_wow_convert_8023_to_80211
               &old_hdr_mask->h_proto,
               sizeof(old_hdr_mask->h_proto));
 
-       /* Caculate new pkt_offset */
+       /* Calculate new pkt_offset */
        if (old->pkt_offset < ETH_ALEN)
                new->pkt_offset = old->pkt_offset +
                        offsetof(struct ieee80211_hdr_3addr, addr1);
@@ -146,7 +146,7 @@ static void ath10k_wow_convert_8023_to_80211
        else
                new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
 
-       /* Caculate new hdr end offset */
+       /* Calculate new hdr end offset */
        if (total_len > ETH_HLEN)
                hdr_80211_end_offset = hdr_len + rfc_len;
        else if (total_len > offsetof(struct ethhdr, h_proto))
index cb59016..5e7ea83 100644 (file)
@@ -389,6 +389,7 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel)
                if (!ik->valid || ik->key_type != WAPI_CRYPT)
                        break;
                /* for WAPI, we need to set the delayed group key, continue: */
+               /* fall through */
        case WPA_PSK_AUTH:
        case WPA2_PSK_AUTH:
        case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
index 1f35230..ceca23a 100644 (file)
@@ -116,7 +116,7 @@ config ATH9K_DFS_CERTIFIED
          except increase code size.
 
 config ATH9K_DYNACK
-       bool "Atheros ath9k ACK timeout estimation algorithm (EXPERIMENTAL)"
+       bool "Atheros ath9k ACK timeout estimation algorithm"
        depends on ATH9K
        default n
        ---help---
index 11d6f97..dae9540 100644 (file)
@@ -586,7 +586,7 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah)
                        REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
                        break;
                }
-               /* else: fall through */
+               /* fall through */
        case 0x1:
        case 0x2:
        case 0x7:
index 7132918..6f32b8d 100644 (file)
@@ -119,7 +119,7 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
                                aModeRefSel = 2;
                        if (aModeRefSel)
                                break;
-                       /* else: fall through */
+                       /* fall through */
                case 1:
                default:
                        aModeRefSel = 0;
index 0fe9c83..9899661 100644 (file)
@@ -1055,17 +1055,15 @@ void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep)
 static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done)
 {
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
-       u32 new_flags, to_set, to_clear;
+       u32 to_set, to_clear;
 
        if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP))
                return;
 
        if (mci->is_2g) {
-               new_flags = MCI_2G_FLAGS;
                to_clear = MCI_2G_FLAGS_CLEAR_MASK;
                to_set = MCI_2G_FLAGS_SET_MASK;
        } else {
-               new_flags = MCI_5G_FLAGS;
                to_clear = MCI_5G_FLAGS_CLEAR_MASK;
                to_set = MCI_5G_FLAGS_SET_MASK;
        }
index 21ba209..0fca44e 100644 (file)
@@ -272,7 +272,7 @@ struct ath_node {
 #endif
        u8 key_idx[4];
 
-       u32 ackto;
+       int ackto;
        struct list_head list;
 };
 
index 7334c9b..f112fa5 100644 (file)
  * ath_dynack_ewma - EWMA (Exponentially Weighted Moving Average) calculation
  *
  */
-static inline u32 ath_dynack_ewma(u32 old, u32 new)
+static inline int ath_dynack_ewma(int old, int new)
 {
-       return (new * (EWMA_DIV - EWMA_LEVEL) + old * EWMA_LEVEL) / EWMA_DIV;
+       if (old > 0)
+               return (new * (EWMA_DIV - EWMA_LEVEL) +
+                       old * EWMA_LEVEL) / EWMA_DIV;
+       else
+               return new;
 }
 
 /**
@@ -82,10 +86,10 @@ static inline bool ath_dynack_bssidmask(struct ath_hw *ah, const u8 *mac)
  */
 static void ath_dynack_compute_ackto(struct ath_hw *ah)
 {
-       struct ath_node *an;
-       u32 to = 0;
-       struct ath_dynack *da = &ah->dynack;
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_dynack *da = &ah->dynack;
+       struct ath_node *an;
+       int to = 0;
 
        list_for_each_entry(an, &da->nodes, list)
                if (an->ackto > to)
@@ -144,7 +148,8 @@ static void ath_dynack_compute_to(struct ath_hw *ah)
                                        an->ackto = ath_dynack_ewma(an->ackto,
                                                                    ackto);
                                        ath_dbg(ath9k_hw_common(ah), DYNACK,
-                                               "%pM to %u\n", dst, an->ackto);
+                                               "%pM to %d [%u]\n", dst,
+                                               an->ackto, ackto);
                                        if (time_is_before_jiffies(da->lto)) {
                                                ath_dynack_compute_ackto(ah);
                                                da->lto = jiffies + COMPUTE_TO;
@@ -166,18 +171,21 @@ static void ath_dynack_compute_to(struct ath_hw *ah)
  * @ah: ath hw
  * @skb: socket buffer
  * @ts: tx status info
+ * @sta: station pointer
  *
  */
 void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
-                            struct ath_tx_status *ts)
+                            struct ath_tx_status *ts,
+                            struct ieee80211_sta *sta)
 {
-       u8 ridx;
        struct ieee80211_hdr *hdr;
        struct ath_dynack *da = &ah->dynack;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       u32 dur = ts->duration;
+       u8 ridx;
 
-       if ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !da->enabled)
+       if (!da->enabled || (info->flags & IEEE80211_TX_CTL_NO_ACK))
                return;
 
        spin_lock_bh(&da->qlock);
@@ -187,11 +195,19 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
        /* late ACK */
        if (ts->ts_status & ATH9K_TXERR_XRETRY) {
                if (ieee80211_is_assoc_req(hdr->frame_control) ||
-                   ieee80211_is_assoc_resp(hdr->frame_control)) {
+                   ieee80211_is_assoc_resp(hdr->frame_control) ||
+                   ieee80211_is_auth(hdr->frame_control)) {
                        ath_dbg(common, DYNACK, "late ack\n");
+
                        ath9k_hw_setslottime(ah, (LATEACK_TO - 3) / 2);
                        ath9k_hw_set_ack_timeout(ah, LATEACK_TO);
                        ath9k_hw_set_cts_timeout(ah, LATEACK_TO);
+                       if (sta) {
+                               struct ath_node *an;
+
+                               an = (struct ath_node *)sta->drv_priv;
+                               an->ackto = -1;
+                       }
                        da->lto = jiffies + LATEACK_DELAY;
                }
 
@@ -202,14 +218,13 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
        ridx = ts->ts_rateindex;
 
        da->st_rbf.ts[da->st_rbf.t_rb].tstamp = ts->ts_tstamp;
-       da->st_rbf.ts[da->st_rbf.t_rb].dur = ts->duration;
        ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_dest, hdr->addr1);
        ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_src, hdr->addr2);
 
        if (!(info->status.rates[ridx].flags & IEEE80211_TX_RC_MCS)) {
-               u32 phy, sifs;
                const struct ieee80211_rate *rate;
                struct ieee80211_tx_rate *rates = info->status.rates;
+               u32 phy;
 
                rate = &common->sbands[info->band].bitrates[rates[ridx].idx];
                if (info->band == NL80211_BAND_2GHZ &&
@@ -218,19 +233,18 @@ void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
                else
                        phy = WLAN_RC_PHY_OFDM;
 
-               sifs = ath_dynack_get_sifs(ah, phy);
-               da->st_rbf.ts[da->st_rbf.t_rb].dur -= sifs;
+               dur -= ath_dynack_get_sifs(ah, phy);
        }
-
-       ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n",
-               hdr->addr1, da->st_rbf.ts[da->st_rbf.t_rb].tstamp,
-               da->st_rbf.ts[da->st_rbf.t_rb].dur, da->st_rbf.h_rb,
-               (da->st_rbf.t_rb + 1) % ATH_DYN_BUF);
+       da->st_rbf.ts[da->st_rbf.t_rb].dur = dur;
 
        INCR(da->st_rbf.t_rb, ATH_DYN_BUF);
        if (da->st_rbf.t_rb == da->st_rbf.h_rb)
                INCR(da->st_rbf.h_rb, ATH_DYN_BUF);
 
+       ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n",
+               hdr->addr1, ts->ts_tstamp, dur, da->st_rbf.h_rb,
+               da->st_rbf.t_rb);
+
        ath_dynack_compute_to(ah);
 
        spin_unlock_bh(&da->qlock);
@@ -251,20 +265,19 @@ void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       if (!ath_dynack_bssidmask(ah, hdr->addr1) || !da->enabled)
+       if (!da->enabled || !ath_dynack_bssidmask(ah, hdr->addr1))
                return;
 
        spin_lock_bh(&da->qlock);
        da->ack_rbf.tstamp[da->ack_rbf.t_rb] = ts;
 
-       ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n",
-               da->ack_rbf.tstamp[da->ack_rbf.t_rb],
-               da->ack_rbf.h_rb, (da->ack_rbf.t_rb + 1) % ATH_DYN_BUF);
-
        INCR(da->ack_rbf.t_rb, ATH_DYN_BUF);
        if (da->ack_rbf.t_rb == da->ack_rbf.h_rb)
                INCR(da->ack_rbf.h_rb, ATH_DYN_BUF);
 
+       ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n",
+               ts, da->ack_rbf.h_rb, da->ack_rbf.t_rb);
+
        ath_dynack_compute_to(ah);
 
        spin_unlock_bh(&da->qlock);
index 6d7bef9..cf60224 100644 (file)
@@ -86,7 +86,8 @@ void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an);
 void ath_dynack_init(struct ath_hw *ah);
 void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, u32 ts);
 void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb,
-                            struct ath_tx_status *ts);
+                            struct ath_tx_status *ts,
+                            struct ieee80211_sta *sta);
 #else
 static inline void ath_dynack_init(struct ath_hw *ah) {}
 static inline void ath_dynack_node_init(struct ath_hw *ah,
@@ -97,7 +98,8 @@ static inline void ath_dynack_sample_ack_ts(struct ath_hw *ah,
                                            struct sk_buff *skb, u32 ts) {}
 static inline void ath_dynack_sample_tx_ts(struct ath_hw *ah,
                                           struct sk_buff *skb,
-                                          struct ath_tx_status *ts) {}
+                                          struct ath_tx_status *ts,
+                                          struct ieee80211_sta *sta) {}
 #endif
 
 #endif /* DYNACK_H */
index bb319f2..8581d91 100644 (file)
@@ -2279,6 +2279,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
        case NL80211_IFTYPE_ADHOC:
                REG_SET_BIT(ah, AR_TXCFG,
                            AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
+               /* fall through */
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
index 25b3fc8..f448d57 100644 (file)
@@ -629,7 +629,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                if (bf == bf->bf_lastbf)
                                        ath_dynack_sample_tx_ts(sc->sc_ah,
                                                                bf->bf_mpdu,
-                                                               ts);
+                                                               ts, sta);
                        }
 
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, sta, ts,
@@ -773,7 +773,8 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
                        memcpy(info->control.rates, bf->rates,
                               sizeof(info->control.rates));
                        ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
-                       ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts);
+                       ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts,
+                                               sta);
                }
                ath_tx_complete_buf(sc, bf, txq, bf_head, sta, ts, txok);
        } else
index 7050632..f7c2f19 100644 (file)
@@ -766,6 +766,7 @@ static void carl9170_rx_untie_data(struct ar9170 *ar, u8 *buf, int len)
 
                        goto drop;
                }
+               /* fall through */
 
        case AR9170_RX_STATUS_MPDU_MIDDLE:
                /*  These are just data + mac status */
index 8c75651..2407931 100644 (file)
@@ -830,10 +830,12 @@ static bool carl9170_tx_rts_check(struct ar9170 *ar,
        case CARL9170_ERP_AUTO:
                if (ampdu)
                        break;
+               /* fall through */
 
        case CARL9170_ERP_MAC80211:
                if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS))
                        break;
+               /* fall through */
 
        case CARL9170_ERP_RTS:
                if (likely(!multi))
@@ -854,6 +856,7 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar,
        case CARL9170_ERP_MAC80211:
                if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
                        break;
+               /* fall through */
 
        case CARL9170_ERP_CTS:
                return true;
index d18e81f..9b2f9f5 100644 (file)
@@ -51,6 +51,19 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
        CHAN60G(4, 0),
 };
 
+static void
+wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
+{
+       kfree(*pdst);
+       *pdst = NULL;
+       *pdst_len = 0;
+       if (src_len > 0) {
+               *pdst = kmemdup(src, src_len, GFP_KERNEL);
+               if (*pdst)
+                       *pdst_len = src_len;
+       }
+}
+
 static int wil_num_supported_channels(struct wil6210_priv *wil)
 {
        int num_channels = ARRAY_SIZE(wil_60ghz_channels);
@@ -1441,11 +1454,19 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
 
        rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
                                params->key, key_usage);
-       if (!rc && !IS_ERR(cs))
+       if (!rc && !IS_ERR(cs)) {
+               /* update local storage used for AP recovery */
+               if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
+                   params->key_len <= WMI_MAX_KEY_LEN) {
+                       vif->gtk_index = key_index;
+                       memcpy(vif->gtk, params->key, params->key_len);
+                       vif->gtk_len = params->key_len;
+               }
                /* in FT set crypto will take place upon receiving
                 * WMI_RING_EN_EVENTID event
                 */
                wil_set_crypto_rx(key_index, key_usage, cs, params);
+       }
 
        return rc;
 }
@@ -1634,6 +1655,14 @@ static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
        u16 len = 0, proberesp_len = 0;
        u8 *ies = NULL, *proberesp;
 
+       /* update local storage used for AP recovery */
+       wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
+                     bcon->probe_resp_len);
+       wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
+                     bcon->proberesp_ies, bcon->proberesp_ies_len);
+       wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
+                     bcon->assocresp_ies, bcon->assocresp_ies_len);
+
        proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
                                                    bcon->probe_resp_len,
                                                    &proberesp_len);
@@ -1735,6 +1764,9 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
        vif->channel = chan;
        vif->hidden_ssid = hidden_ssid;
        vif->pbss = pbss;
+       vif->bi = bi;
+       memcpy(vif->ssid, ssid, ssid_len);
+       vif->ssid_len = ssid_len;
 
        netif_carrier_on(ndev);
        if (!wil_has_other_active_ifaces(wil, ndev, false, true))
@@ -1761,11 +1793,64 @@ out:
        return rc;
 }
 
+void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
+{
+       int rc, i;
+       struct wiphy *wiphy = wil_to_wiphy(wil);
+
+       for (i = 0; i < wil->max_vifs; i++) {
+               struct wil6210_vif *vif = wil->vifs[i];
+               struct net_device *ndev;
+               struct cfg80211_beacon_data bcon = {};
+               struct key_params key_params = {};
+
+               if (!vif || vif->ssid_len == 0)
+                       continue;
+
+               ndev = vif_to_ndev(vif);
+               bcon.proberesp_ies = vif->proberesp_ies;
+               bcon.assocresp_ies = vif->assocresp_ies;
+               bcon.probe_resp = vif->proberesp;
+               bcon.proberesp_ies_len = vif->proberesp_ies_len;
+               bcon.assocresp_ies_len = vif->assocresp_ies_len;
+               bcon.probe_resp_len = vif->proberesp_len;
+
+               wil_info(wil,
+                        "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
+                        i, vif->privacy, vif->bi, vif->channel,
+                        vif->hidden_ssid, vif->pbss);
+               wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
+                                 vif->ssid, vif->ssid_len, true);
+               rc = _wil_cfg80211_start_ap(wiphy, ndev,
+                                           vif->ssid, vif->ssid_len,
+                                           vif->privacy, vif->bi,
+                                           vif->channel, &bcon,
+                                           vif->hidden_ssid, vif->pbss);
+               if (rc) {
+                       wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
+                       continue;
+               }
+
+               if (!vif->privacy || vif->gtk_len == 0)
+                       continue;
+
+               key_params.key = vif->gtk;
+               key_params.key_len = vif->gtk_len;
+               key_params.seq_len = IEEE80211_GCMP_PN_LEN;
+               rc = wil_cfg80211_add_key(wiphy, ndev, vif->gtk_index, false,
+                                         NULL, &key_params);
+               if (rc)
+                       wil_err(wil, "vif %d recovery add key failed (%d)\n",
+                               i, rc);
+       }
+}
+
 static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
                                      struct net_device *ndev,
                                      struct cfg80211_beacon_data *bcon)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
        struct wil6210_vif *vif = ndev_to_vif(ndev);
        int rc;
        u32 privacy = 0;
@@ -1778,15 +1863,16 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
                             bcon->tail_len))
                privacy = 1;
 
+       memcpy(vif->ssid, wdev->ssid, wdev->ssid_len);
+       vif->ssid_len = wdev->ssid_len;
+
        /* in case privacy has changed, need to restart the AP */
        if (vif->privacy != privacy) {
-               struct wireless_dev *wdev = ndev->ieee80211_ptr;
-
                wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
                             vif->privacy, privacy);
 
-               rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
-                                           wdev->ssid_len, privacy,
+               rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
+                                           vif->ssid_len, privacy,
                                            wdev->beacon_interval,
                                            vif->channel, bcon,
                                            vif->hidden_ssid,
@@ -1876,6 +1962,12 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 
        wmi_pcp_stop(vif);
        clear_bit(wil_vif_ft_roam, vif->status);
+       vif->ssid_len = 0;
+       wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
+       wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
+       wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
+       memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
+       vif->gtk_len = 0;
 
        if (last)
                __wil_down(wil);
@@ -1923,7 +2015,7 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
                     params->mac, params->reason_code, vif->mid);
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, params->mac, params->reason_code, false);
+       wil6210_disconnect(vif, params->mac, params->reason_code);
        mutex_unlock(&wil->mutex);
 
        return 0;
index aa50813..20dd4d0 100644 (file)
@@ -664,10 +664,10 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        enum { max_count = 4096 };
        struct wil_blob_wrapper *wil_blob = file->private_data;
        struct wil6210_priv *wil = wil_blob->wil;
-       loff_t pos = *ppos;
+       loff_t aligned_pos, pos = *ppos;
        size_t available = wil_blob->blob.size;
        void *buf;
-       size_t ret;
+       size_t unaligned_bytes, aligned_count, ret;
        int rc;
 
        if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
@@ -685,7 +685,12 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        if (count > max_count)
                count = max_count;
 
-       buf = kmalloc(count, GFP_KERNEL);
+       /* set pos to 4 bytes aligned */
+       unaligned_bytes = pos % 4;
+       aligned_pos = pos - unaligned_bytes;
+       aligned_count = count + unaligned_bytes;
+
+       buf = kmalloc(aligned_count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
@@ -696,9 +701,9 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
        }
 
        wil_memcpy_fromio_32(buf, (const void __iomem *)
-                            wil_blob->blob.data + pos, count);
+                            wil_blob->blob.data + aligned_pos, aligned_count);
 
-       ret = copy_to_user(user_buf, buf, count);
+       ret = copy_to_user(user_buf, buf + unaligned_bytes, count);
 
        wil_pm_runtime_put(wil);
 
@@ -962,6 +967,8 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
        int rc;
        void *frame;
 
+       memset(&params, 0, sizeof(params));
+
        if (!len)
                return -EINVAL;
 
index 398900a..ba6a2ee 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/moduleparam.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
 
 #include "wil6210.h"
 #include "txrx.h"
@@ -80,7 +81,7 @@ static const struct kernel_param_ops mtu_max_ops = {
 module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, 0444);
 MODULE_PARM_DESC(mtu_max, " Max MTU value.");
 
-static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint rx_ring_order;
 static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
 static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
 
@@ -214,8 +215,21 @@ static void wil_ring_fini_tx(struct wil6210_priv *wil, int id)
        wil->txrx_ops.ring_fini_tx(wil, ring);
 }
 
-static void wil_disconnect_cid(struct wil6210_vif *vif, int cid,
-                              u16 reason_code, bool from_event)
+static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
+{
+       int i;
+
+       for (i = 0; i < WIL6210_MAX_CID; i++) {
+               if (wil->sta[i].mid == mid &&
+                   wil->sta[i].status == wil_sta_connected)
+                       return true;
+       }
+
+       return false;
+}
+
+static void wil_disconnect_cid_complete(struct wil6210_vif *vif, int cid,
+                                       u16 reason_code)
 __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
 {
        uint i;
@@ -226,24 +240,14 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
        int min_ring_id = wil_get_min_tx_ring_id(wil);
 
        might_sleep();
-       wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
+       wil_dbg_misc(wil,
+                    "disconnect_cid_complete: CID %d, MID %d, status %d\n",
                     cid, sta->mid, sta->status);
-       /* inform upper/lower layers */
+       /* inform upper layers */
        if (sta->status != wil_sta_unused) {
                if (vif->mid != sta->mid) {
                        wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
                                vif->mid);
-                       /* let FW override sta->mid but be more strict with
-                        * user space requests
-                        */
-                       if (!from_event)
-                               return;
-               }
-               if (!from_event) {
-                       bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
-                                               disable_ap_sme : false;
-                       wmi_disconnect_sta(vif, sta->addr, reason_code,
-                                          true, del_sta);
                }
 
                switch (wdev->iftype) {
@@ -283,36 +287,20 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
        sta->stats.tx_latency_min_us = U32_MAX;
 }
 
-static bool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
-               if (wil->sta[i].mid == mid &&
-                   wil->sta[i].status == wil_sta_connected)
-                       return true;
-       }
-
-       return false;
-}
-
-static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                               u16 reason_code, bool from_event)
+static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
+                                        const u8 *bssid, u16 reason_code)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
        int cid = -ENOENT;
        struct net_device *ndev;
        struct wireless_dev *wdev;
 
-       if (unlikely(!vif))
-               return;
-
        ndev = vif_to_ndev(vif);
        wdev = vif_to_wdev(vif);
 
        might_sleep();
-       wil_info(wil, "bssid=%pM, reason=%d, ev%s\n", bssid,
-                reason_code, from_event ? "+" : "-");
+       wil_info(wil, "disconnect_complete: bssid=%pM, reason=%d\n",
+                bssid, reason_code);
 
        /* Cases are:
         * - disconnect single STA, still connected
@@ -327,14 +315,15 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
        if (bssid && !is_broadcast_ether_addr(bssid) &&
            !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
                cid = wil_find_cid(wil, vif->mid, bssid);
-               wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
+               wil_dbg_misc(wil,
+                            "Disconnect complete %pM, CID=%d, reason=%d\n",
                             bssid, cid, reason_code);
                if (cid >= 0) /* disconnect 1 peer */
-                       wil_disconnect_cid(vif, cid, reason_code, from_event);
+                       wil_disconnect_cid_complete(vif, cid, reason_code);
        } else { /* all */
-               wil_dbg_misc(wil, "Disconnect all\n");
+               wil_dbg_misc(wil, "Disconnect complete all\n");
                for (cid = 0; cid < WIL6210_MAX_CID; cid++)
-                       wil_disconnect_cid(vif, cid, reason_code, from_event);
+                       wil_disconnect_cid_complete(vif, cid, reason_code);
        }
 
        /* link state */
@@ -380,6 +369,84 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
        }
 }
 
+static int wil_disconnect_cid(struct wil6210_vif *vif, int cid,
+                             u16 reason_code)
+{
+       struct wil6210_priv *wil = vif_to_wil(vif);
+       struct wireless_dev *wdev = vif_to_wdev(vif);
+       struct wil_sta_info *sta = &wil->sta[cid];
+       bool del_sta = false;
+
+       might_sleep();
+       wil_dbg_misc(wil, "disconnect_cid: CID %d, MID %d, status %d\n",
+                    cid, sta->mid, sta->status);
+
+       if (sta->status == wil_sta_unused)
+               return 0;
+
+       if (vif->mid != sta->mid) {
+               wil_err(wil, "STA MID mismatch with VIF MID(%d)\n", vif->mid);
+               return -EINVAL;
+       }
+
+       /* inform lower layers */
+       if (wdev->iftype == NL80211_IFTYPE_AP && disable_ap_sme)
+               del_sta = true;
+
+       /* disconnect by sending command disconnect/del_sta and wait
+        * synchronously for WMI_DISCONNECT_EVENTID event.
+        */
+       return wmi_disconnect_sta(vif, sta->addr, reason_code, del_sta);
+}
+
+static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
+                               u16 reason_code)
+{
+       struct wil6210_priv *wil;
+       struct net_device *ndev;
+       struct wireless_dev *wdev;
+       int cid = -ENOENT;
+
+       if (unlikely(!vif))
+               return;
+
+       wil = vif_to_wil(vif);
+       ndev = vif_to_ndev(vif);
+       wdev = vif_to_wdev(vif);
+
+       might_sleep();
+       wil_info(wil, "disconnect bssid=%pM, reason=%d\n", bssid, reason_code);
+
+       /* Cases are:
+        * - disconnect single STA, still connected
+        * - disconnect single STA, already disconnected
+        * - disconnect all
+        *
+        * For "disconnect all", there are 3 options:
+        * - bssid == NULL
+        * - bssid is broadcast address (ff:ff:ff:ff:ff:ff)
+        * - bssid is our MAC address
+        */
+       if (bssid && !is_broadcast_ether_addr(bssid) &&
+           !ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
+               cid = wil_find_cid(wil, vif->mid, bssid);
+               wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
+                            bssid, cid, reason_code);
+               if (cid >= 0) /* disconnect 1 peer */
+                       wil_disconnect_cid(vif, cid, reason_code);
+       } else { /* all */
+               wil_dbg_misc(wil, "Disconnect all\n");
+               for (cid = 0; cid < WIL6210_MAX_CID; cid++)
+                       wil_disconnect_cid(vif, cid, reason_code);
+       }
+
+       /* call event handler manually after processing wmi_call,
+        * to avoid deadlock - disconnect event handler acquires
+        * wil->mutex while it is already held here
+        */
+       _wil6210_disconnect_complete(vif, bssid, reason_code);
+}
+
 void wil_disconnect_worker(struct work_struct *work)
 {
        struct wil6210_vif *vif = container_of(work,
@@ -485,10 +552,11 @@ static void wil_fw_error_worker(struct work_struct *work)
        if (wil_wait_for_recovery(wil) != 0)
                return;
 
+       rtnl_lock();
        mutex_lock(&wil->mutex);
        /* Needs adaptation for multiple VIFs
         * need to go over all VIFs and consider the appropriate
-        * recovery.
+        * recovery because each one can have different iftype.
         */
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
@@ -500,15 +568,24 @@ static void wil_fw_error_worker(struct work_struct *work)
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               wil_info(wil, "No recovery for AP-like interface\n");
-               /* recovery in these modes is done by upper layers */
+               if (no_fw_recovery) /* upper layers do recovery */
+                       break;
+               /* silent recovery, upper layers will see disconnect */
+               __wil_down(wil);
+               __wil_up(wil);
+               mutex_unlock(&wil->mutex);
+               wil_cfg80211_ap_recovery(wil);
+               mutex_lock(&wil->mutex);
+               wil_info(wil, "... completed\n");
                break;
        default:
                wil_err(wil, "No recovery - unknown interface type %d\n",
                        wdev->iftype);
                break;
        }
+
        mutex_unlock(&wil->mutex);
+       rtnl_unlock();
 }
 
 static int wil_find_free_ring(struct wil6210_priv *wil)
@@ -694,20 +771,41 @@ void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
  * @vif: virtual interface context
  * @bssid: peer to disconnect, NULL to disconnect all
  * @reason_code: Reason code for the Disassociation frame
- * @from_event: whether is invoked from FW event handler
  *
- * Disconnect and release associated resources. If invoked not from the
- * FW event handler, issue WMI command(s) to trigger MAC disconnect.
+ * Disconnect and release associated resources. Issue WMI
+ * command(s) to trigger MAC disconnect. When command was issued
+ * successfully, call the wil6210_disconnect_complete function
+ * to handle the event synchronously
  */
 void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                       u16 reason_code, bool from_event)
+                       u16 reason_code)
+{
+       struct wil6210_priv *wil = vif_to_wil(vif);
+
+       wil_dbg_misc(wil, "disconnecting\n");
+
+       del_timer_sync(&vif->connect_timer);
+       _wil6210_disconnect(vif, bssid, reason_code);
+}
+
+/**
+ * wil6210_disconnect_complete - handle disconnect event
+ * @vif: virtual interface context
+ * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
+ *
+ * Release associated resources and indicate upper layers the
+ * connection is terminated.
+ */
+void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
+                                u16 reason_code)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
 
-       wil_dbg_misc(wil, "disconnect\n");
+       wil_dbg_misc(wil, "got disconnect\n");
 
        del_timer_sync(&vif->connect_timer);
-       _wil6210_disconnect(vif, bssid, reason_code, from_event);
+       _wil6210_disconnect_complete(vif, bssid, reason_code);
 }
 
 void wil_priv_deinit(struct wil6210_priv *wil)
@@ -998,10 +1096,13 @@ static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
 
        wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name);
 
-       /* Clear MAC link up */
-       wil_s(wil, RGF_HP_CTRL, BIT(15));
-       wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_HPAL_PERST_FROM_PAD);
-       wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
+       if (wil->hw_version < HW_VER_TALYN) {
+               /* Clear MAC link up */
+               wil_s(wil, RGF_HP_CTRL, BIT(15));
+               wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0,
+                     BIT_HPAL_PERST_FROM_PAD);
+               wil_s(wil, RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
+       }
 
        wil_halt_cpu(wil);
 
@@ -1398,8 +1499,15 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
        wil6210_clear_irq(wil);
        /* CAF_ICR - clear and mask */
        /* it is W1C, clear by writing back same value */
-       wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
-       wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+       if (wil->hw_version < HW_VER_TALYN_MB) {
+               wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
+               wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+       } else {
+               wil_s(wil,
+                     RGF_CAF_ICR_TALYN_MB + offsetof(struct RGF_ICR, ICR), 0);
+               wil_w(wil, RGF_CAF_ICR_TALYN_MB +
+                     offsetof(struct RGF_ICR, IMV), ~0);
+       }
        /* clear PAL_UNIT_ICR (potential D0->D3 leftover)
         * In Talyn-MB host cannot access this register due to
         * access control, hence PAL_UNIT_ICR is cleared by the FW
@@ -1511,7 +1619,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
                if (vif) {
                        cancel_work_sync(&vif->disconnect_worker);
                        wil6210_disconnect(vif, NULL,
-                                          WLAN_REASON_DEAUTH_LEAVING, false);
+                                          WLAN_REASON_DEAUTH_LEAVING);
                }
        }
        wil_bcast_fini_all(wil);
@@ -1681,7 +1789,12 @@ int __wil_up(struct wil6210_priv *wil)
                return rc;
 
        /* Rx RING. After MAC and beacon */
-       rc = wil->txrx_ops.rx_init(wil, 1 << rx_ring_order);
+       if (rx_ring_order == 0)
+               rx_ring_order = wil->hw_version < HW_VER_TALYN_MB ?
+                       WIL_RX_RING_SIZE_ORDER_DEFAULT :
+                       WIL_RX_RING_SIZE_ORDER_TALYN_DEFAULT;
+
+       rc = wil->txrx_ops.rx_init(wil, rx_ring_order);
        if (rc)
                return rc;
 
index 7a78a06..b4e0eb1 100644 (file)
@@ -345,8 +345,7 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name,
        ndev->ieee80211_ptr = wdev;
        ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
                            NETIF_F_SG | NETIF_F_GRO |
-                           NETIF_F_TSO | NETIF_F_TSO6 |
-                           NETIF_F_RXHASH;
+                           NETIF_F_TSO | NETIF_F_TSO6;
 
        ndev->features |= ndev->hw_features;
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
@@ -513,7 +512,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
        }
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING);
        mutex_unlock(&wil->mutex);
 
        ndev = vif_to_ndev(vif);
index cc5f263..3e1c831 100644 (file)
@@ -743,14 +743,6 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
 
        stats = &wil->sta[cid].stats;
 
-       if (ndev->features & NETIF_F_RXHASH)
-               /* fake L4 to ensure it won't be re-calculated later
-                * set hash to any non-zero value to activate rps
-                * mechanism, core will be chosen according
-                * to user-level rps configuration.
-                */
-               skb_set_hash(skb, 1, PKT_HASH_TYPE_L4);
-
        skb_orphan(skb);
 
        if (security && (wil->txrx_ops.rx_crypto_check(wil, skb) != 0)) {
@@ -880,7 +872,7 @@ static void wil_rx_buf_len_init(struct wil6210_priv *wil)
        }
 }
 
-static int wil_rx_init(struct wil6210_priv *wil, u16 size)
+static int wil_rx_init(struct wil6210_priv *wil, uint order)
 {
        struct wil_ring *vring = &wil->ring_rx;
        int rc;
@@ -894,7 +886,7 @@ static int wil_rx_init(struct wil6210_priv *wil, u16 size)
 
        wil_rx_buf_len_init(wil);
 
-       vring->size = size;
+       vring->size = 1 << order;
        vring->is_rx = true;
        rc = wil_vring_alloc(wil, vring);
        if (rc)
@@ -1403,6 +1395,8 @@ found:
                        wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
                        wil_set_da_for_vring(wil, skb2, i);
                        wil_tx_ring(wil, vif, v2, skb2);
+                       /* successful call to wil_tx_ring takes skb2 ref */
+                       dev_kfree_skb_any(skb2);
                } else {
                        wil_err(wil, "skb_copy failed\n");
                }
index 2bbae75..05a8348 100644 (file)
@@ -160,7 +160,7 @@ static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil,
                                   struct wil_ring *ring, u32 i)
 {
        struct device *dev = wil_to_dev(wil);
-       unsigned int sz = ALIGN(wil->rx_buf_len, 4);
+       unsigned int sz = wil->rx_buf_len;
        dma_addr_t pa;
        u16 buff_id;
        struct list_head *active = &wil->rx_buff_mgmt.active;
@@ -234,9 +234,10 @@ static int wil_rx_refill_edma(struct wil6210_priv *wil)
        struct wil_ring *ring = &wil->ring_rx;
        u32 next_head;
        int rc = 0;
-       u32 swtail = *ring->edma_rx_swtail.va;
+       ring->swtail = *ring->edma_rx_swtail.va;
 
-       for (; next_head = wil_ring_next_head(ring), (next_head != swtail);
+       for (; next_head = wil_ring_next_head(ring),
+            (next_head != ring->swtail);
             ring->swhead = next_head) {
                rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead);
                if (unlikely(rc)) {
@@ -264,43 +265,26 @@ static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil,
                                              struct wil_ring *ring)
 {
        struct device *dev = wil_to_dev(wil);
-       u32 next_tail;
-       u32 swhead = (ring->swhead + 1) % ring->size;
+       struct list_head *active = &wil->rx_buff_mgmt.active;
        dma_addr_t pa;
-       u16 dmalen;
 
-       for (; next_tail = wil_ring_next_tail(ring), (next_tail != swhead);
-            ring->swtail = next_tail) {
-               struct wil_rx_enhanced_desc dd, *d = &dd;
-               struct wil_rx_enhanced_desc *_d =
-                       (struct wil_rx_enhanced_desc *)
-                       &ring->va[ring->swtail].rx.enhanced;
-               struct sk_buff *skb;
-               u16 buff_id;
+       while (!list_empty(active)) {
+               struct wil_rx_buff *rx_buff =
+                       list_first_entry(active, struct wil_rx_buff, list);
+               struct sk_buff *skb = rx_buff->skb;
 
-               *d = *_d;
-
-               /* Extract the SKB from the rx_buff management array */
-               buff_id = __le16_to_cpu(d->mac.buff_id);
-               if (buff_id >= wil->rx_buff_mgmt.size) {
-                       wil_err(wil, "invalid buff_id %d\n", buff_id);
-                       continue;
-               }
-               skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
-               wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL;
                if (unlikely(!skb)) {
-                       wil_err(wil, "No Rx skb at buff_id %d\n", buff_id);
+                       wil_err(wil, "No Rx skb at buff_id %d\n", rx_buff->id);
                } else {
-                       pa = wil_rx_desc_get_addr_edma(&d->dma);
-                       dmalen = le16_to_cpu(d->dma.length);
-                       dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
-
+                       rx_buff->skb = NULL;
+                       memcpy(&pa, skb->cb, sizeof(pa));
+                       dma_unmap_single(dev, pa, wil->rx_buf_len,
+                                        DMA_FROM_DEVICE);
                        kfree_skb(skb);
                }
 
                /* Move the buffer from the active to the free list */
-               list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list,
-                         &wil->rx_buff_mgmt.free);
+               list_move(&rx_buff->list, &wil->rx_buff_mgmt.free);
        }
 }
 
@@ -357,8 +341,8 @@ static int wil_init_rx_sring(struct wil6210_priv *wil,
        struct wil_status_ring *sring = &wil->srings[ring_id];
        int rc;
 
-       wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n", sring->size,
-                    ring_id);
+       wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n",
+                    status_ring_size, ring_id);
 
        memset(&sring->rx_data, 0, sizeof(sring->rx_data));
 
@@ -602,20 +586,20 @@ static bool wil_is_rx_idle_edma(struct wil6210_priv *wil)
 
 static void wil_rx_buf_len_init_edma(struct wil6210_priv *wil)
 {
+       /* RX buffer size must be aligned to 4 bytes */
        wil->rx_buf_len = rx_large_buf ?
                WIL_MAX_ETH_MTU : WIL_EDMA_RX_BUF_LEN_DEFAULT;
 }
 
-static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
+static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order)
 {
-       u16 status_ring_size;
+       u16 status_ring_size, desc_ring_size = 1 << desc_ring_order;
        struct wil_ring *ring = &wil->ring_rx;
        int rc;
        size_t elem_size = wil->use_compressed_rx_status ?
                sizeof(struct wil_rx_status_compressed) :
                sizeof(struct wil_rx_status_extended);
        int i;
-       u16 max_rx_pl_per_desc;
 
        /* In SW reorder one must use extended status messages */
        if (wil->use_compressed_rx_status && !wil->use_rx_hw_reordering) {
@@ -623,7 +607,12 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
                        "compressed RX status cannot be used with SW reorder\n");
                return -EINVAL;
        }
-
+       if (wil->rx_status_ring_order <= desc_ring_order)
+               /* make sure sring is larger than desc ring */
+               wil->rx_status_ring_order = desc_ring_order + 1;
+       if (wil->rx_buff_id_count <= desc_ring_size)
+               /* make sure we will not run out of buff_ids */
+               wil->rx_buff_id_count = desc_ring_size + 512;
        if (wil->rx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN ||
            wil->rx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX)
                wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT;
@@ -636,8 +625,6 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
 
        wil_rx_buf_len_init_edma(wil);
 
-       max_rx_pl_per_desc = ALIGN(wil->rx_buf_len, 4);
-
        /* Use debugfs dbg_num_rx_srings if set, reserve one sring for TX */
        if (wil->num_rx_status_rings > WIL6210_MAX_STATUS_RINGS - 1)
                wil->num_rx_status_rings = WIL6210_MAX_STATUS_RINGS - 1;
@@ -645,7 +632,7 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, u16 desc_ring_size)
        wil_dbg_misc(wil, "rx_init: allocate %d status rings\n",
                     wil->num_rx_status_rings);
 
-       rc = wil_wmi_cfg_def_rx_offload(wil, max_rx_pl_per_desc);
+       rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len);
        if (rc)
                return rc;
 
@@ -834,23 +821,24 @@ static int wil_rx_error_check_edma(struct wil6210_priv *wil,
                wil_dbg_txrx(wil, "L2 RX error, l2_rx_status=0x%x\n",
                             l2_rx_status);
                /* Due to HW issue, KEY error will trigger a MIC error */
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_MIC) {
-                       wil_dbg_txrx(wil,
-                                    "L2 MIC/KEY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_MIC) {
+                       wil_err_ratelimited(wil,
+                                           "L2 MIC/KEY error, dropping packet\n");
                        stats->rx_mic_error++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_KEY) {
-                       wil_dbg_txrx(wil, "L2 KEY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_KEY) {
+                       wil_err_ratelimited(wil,
+                                           "L2 KEY error, dropping packet\n");
                        stats->rx_key_error++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_REPLAY) {
-                       wil_dbg_txrx(wil,
-                                    "L2 REPLAY error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_REPLAY) {
+                       wil_err_ratelimited(wil,
+                                           "L2 REPLAY error, dropping packet\n");
                        stats->rx_replay++;
                }
-               if (l2_rx_status & WIL_RX_EDMA_ERROR_AMSDU) {
-                       wil_dbg_txrx(wil,
-                                    "L2 AMSDU error, dropping packet\n");
+               if (l2_rx_status == WIL_RX_EDMA_ERROR_AMSDU) {
+                       wil_err_ratelimited(wil,
+                                           "L2 AMSDU error, dropping packet\n");
                        stats->rx_amsdu_error++;
                }
                return -EFAULT;
@@ -881,7 +869,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
        struct sk_buff *skb;
        dma_addr_t pa;
        struct wil_ring_rx_data *rxdata = &sring->rx_data;
-       unsigned int sz = ALIGN(wil->rx_buf_len, 4);
+       unsigned int sz = wil->rx_buf_len;
        struct wil_net_stats *stats = NULL;
        u16 dmalen;
        int cid;
index a7fe929..343516a 100644 (file)
@@ -23,9 +23,9 @@
 #define WIL_SRING_SIZE_ORDER_MIN       (WIL_RING_SIZE_ORDER_MIN)
 #define WIL_SRING_SIZE_ORDER_MAX       (WIL_RING_SIZE_ORDER_MAX)
 /* RX sring order should be bigger than RX ring order */
-#define WIL_RX_SRING_SIZE_ORDER_DEFAULT        (11)
+#define WIL_RX_SRING_SIZE_ORDER_DEFAULT        (12)
 #define WIL_TX_SRING_SIZE_ORDER_DEFAULT        (12)
-#define WIL_RX_BUFF_ARR_SIZE_DEFAULT (1536)
+#define WIL_RX_BUFF_ARR_SIZE_DEFAULT (2600)
 
 #define WIL_DEFAULT_RX_STATUS_RING_ID 0
 #define WIL_RX_DESC_RING_ID 0
index abb8201..0f3be3f 100644 (file)
@@ -81,6 +81,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL_TX_Q_LEN_DEFAULT           (4000)
 #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
+#define WIL_RX_RING_SIZE_ORDER_TALYN_DEFAULT   (11)
 #define WIL_TX_RING_SIZE_ORDER_DEFAULT (12)
 #define WIL_BCAST_RING_SIZE_ORDER_DEFAULT      (7)
 #define WIL_BCAST_MCS0_LIMIT           (1024) /* limit for MCS0 frame size */
@@ -319,6 +320,7 @@ struct RGF_ICR {
 /* MAC timer, usec, for packet lifetime */
 #define RGF_MAC_MTRL_COUNTER_0         (0x886aa8)
 
+#define RGF_CAF_ICR_TALYN_MB           (0x8893d4) /* struct RGF_ICR */
 #define RGF_CAF_ICR                    (0x88946c) /* struct RGF_ICR */
 #define RGF_CAF_OSC_CONTROL            (0x88afa4)
        #define BIT_CAF_OSC_XTAL_EN             BIT(0)
@@ -613,7 +615,7 @@ struct wil_txrx_ops {
                              int cid, int tid);
        irqreturn_t (*irq_tx)(int irq, void *cookie);
        /* RX ops */
-       int (*rx_init)(struct wil6210_priv *wil, u16 ring_size);
+       int (*rx_init)(struct wil6210_priv *wil, uint ring_order);
        void (*rx_fini)(struct wil6210_priv *wil);
        int (*wmi_addba_rx_resp)(struct wil6210_priv *wil, u8 mid, u8 cid,
                                 u8 tid, u8 token, u16 status, bool amsdu,
@@ -848,6 +850,14 @@ struct wil6210_vif {
        u8 hidden_ssid; /* relevant in AP mode */
        u32 ap_isolate; /* no intra-BSS communication */
        bool pbss;
+       int bi;
+       u8 *proberesp, *proberesp_ies, *assocresp_ies;
+       size_t proberesp_len, proberesp_ies_len, assocresp_ies_len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       size_t ssid_len;
+       u8 gtk_index;
+       u8 gtk[WMI_MAX_KEY_LEN];
+       size_t gtk_len;
        int bcast_ring;
        struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */
        int locally_generated_disc; /* relevant in STA mode */
@@ -1220,8 +1230,8 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct wil_ring *vring);
 int wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie);
 int wmi_rxon(struct wil6210_priv *wil, bool on);
 int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
-int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
-                      u16 reason, bool full_disconnect, bool del_sta);
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason,
+                      bool del_sta);
 int wmi_addba(struct wil6210_priv *wil, u8 mid,
              u8 ringid, u8 size, u16 timeout);
 int wmi_delba_tx(struct wil6210_priv *wil, u8 mid, u8 ringid, u16 reason);
@@ -1276,6 +1286,7 @@ int wmi_stop_discovery(struct wil6210_vif *vif);
 int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                         struct cfg80211_mgmt_tx_params *params,
                         u64 *cookie);
+void wil_cfg80211_ap_recovery(struct wil6210_priv *wil);
 int wil_cfg80211_iface_combinations_from_fw(
        struct wil6210_priv *wil,
        const struct wil_fw_record_concurrency *conc);
@@ -1306,7 +1317,9 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync);
 void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync);
 void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps);
 void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
-                       u16 reason_code, bool from_event);
+                       u16 reason_code);
+void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid,
+                                u16 reason_code);
 void wil_probe_client_flush(struct wil6210_vif *vif);
 void wil_probe_client_worker(struct work_struct *work);
 void wil_disconnect_worker(struct work_struct *work);
index 4859f0e..345f059 100644 (file)
@@ -1018,7 +1018,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
                wil_err(wil, "config tx vring failed for CID %d, rc (%d)\n",
                        evt->cid, rc);
                wmi_disconnect_sta(vif, wil->sta[evt->cid].addr,
-                                  WLAN_REASON_UNSPECIFIED, false, false);
+                                  WLAN_REASON_UNSPECIFIED, false);
        } else {
                wil_info(wil, "successful connection to CID %d\n", evt->cid);
        }
@@ -1112,7 +1112,24 @@ static void wmi_evt_disconnect(struct wil6210_vif *vif, int id,
        }
 
        mutex_lock(&wil->mutex);
-       wil6210_disconnect(vif, evt->bssid, reason_code, true);
+       wil6210_disconnect_complete(vif, evt->bssid, reason_code);
+       if (disable_ap_sme) {
+               struct wireless_dev *wdev = vif_to_wdev(vif);
+               struct net_device *ndev = vif_to_ndev(vif);
+
+               /* disconnect event in disable_ap_sme mode means link loss */
+               switch (wdev->iftype) {
+               /* AP-like interface */
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_P2P_GO:
+                       /* notify hostapd about link loss */
+                       cfg80211_cqm_pktloss_notify(ndev, evt->bssid, 0,
+                                                   GFP_KERNEL);
+                       break;
+               default:
+                       break;
+               }
+       }
        mutex_unlock(&wil->mutex);
 }
 
@@ -1637,7 +1654,7 @@ wmi_evt_auth_status(struct wil6210_vif *vif, int id, void *d, int len)
        return;
 
 fail:
-       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 static void
@@ -1766,7 +1783,7 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len)
        return;
 
 fail:
-       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID, false);
+       wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 /**
@@ -1949,16 +1966,17 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
 {
        int rc;
        unsigned long remain;
+       ulong flags;
 
        mutex_lock(&wil->wmi_mutex);
 
-       spin_lock(&wil->wmi_ev_lock);
+       spin_lock_irqsave(&wil->wmi_ev_lock, flags);
        wil->reply_id = reply_id;
        wil->reply_mid = mid;
        wil->reply_buf = reply;
        wil->reply_size = reply_size;
        reinit_completion(&wil->wmi_call);
-       spin_unlock(&wil->wmi_ev_lock);
+       spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 
        rc = __wmi_send(wil, cmdid, mid, buf, len);
        if (rc)
@@ -1978,12 +1996,12 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len,
        }
 
 out:
-       spin_lock(&wil->wmi_ev_lock);
+       spin_lock_irqsave(&wil->wmi_ev_lock, flags);
        wil->reply_id = 0;
        wil->reply_mid = U8_MAX;
        wil->reply_buf = NULL;
        wil->reply_size = 0;
-       spin_unlock(&wil->wmi_ev_lock);
+       spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 
        mutex_unlock(&wil->wmi_mutex);
 
@@ -2560,12 +2578,11 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
        return 0;
 }
 
-int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
-                      u16 reason, bool full_disconnect, bool del_sta)
+int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason,
+                      bool del_sta)
 {
        struct wil6210_priv *wil = vif_to_wil(vif);
        int rc;
-       u16 reason_code;
        struct wmi_disconnect_sta_cmd disc_sta_cmd = {
                .disconnect_reason = cpu_to_le16(reason),
        };
@@ -2598,21 +2615,8 @@ int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac,
                wil_fw_error_recovery(wil);
                return rc;
        }
+       wil->sinfo_gen++;
 
-       if (full_disconnect) {
-               /* call event handler manually after processing wmi_call,
-                * to avoid deadlock - disconnect event handler acquires
-                * wil->mutex while it is already held here
-                */
-               reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
-
-               wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
-                           reply.evt.bssid, reason_code,
-                           reply.evt.disconnect_reason);
-
-               wil->sinfo_gen++;
-               wil6210_disconnect(vif, reply.evt.bssid, reason_code, true);
-       }
        return 0;
 }
 
@@ -3145,7 +3149,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
 
                if (mid == MID_BROADCAST)
                        mid = 0;
-               if (mid >= wil->max_vifs) {
+               if (mid >= ARRAY_SIZE(wil->vifs) || mid >= wil->max_vifs) {
                        wil_dbg_wmi(wil, "invalid mid %d, event skipped\n",
                                    mid);
                        return;
index fba8560..3e41457 100644 (file)
@@ -4,6 +4,7 @@ config B43
        select BCMA if B43_BCMA
        select SSB if B43_SSB
        select FW_LOADER
+       select CORDIC
        ---help---
          b43 is a driver for the Broadcom 43xx series wireless devices.
 
index 85f2ca9..98c4fa5 100644 (file)
@@ -604,50 +604,3 @@ void b43_phy_force_clock(struct b43_wldev *dev, bool force)
 #endif
        }
 }
-
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */
-struct b43_c32 b43_cordic(int theta)
-{
-       static const u32 arctg[] = {
-               2949120, 1740967, 919879, 466945, 234379, 117304,
-                 58666,   29335,  14668,   7334,   3667,   1833,
-                   917,     458,    229,    115,     57,     29,
-       };
-       u8 i;
-       s32 tmp;
-       s8 signx = 1;
-       u32 angle = 0;
-       struct b43_c32 ret = { .i = 39797, .q = 0, };
-
-       while (theta > (180 << 16))
-               theta -= (360 << 16);
-       while (theta < -(180 << 16))
-               theta += (360 << 16);
-
-       if (theta > (90 << 16)) {
-               theta -= (180 << 16);
-               signx = -1;
-       } else if (theta < -(90 << 16)) {
-               theta += (180 << 16);
-               signx = -1;
-       }
-
-       for (i = 0; i <= 17; i++) {
-               if (theta > angle) {
-                       tmp = ret.i - (ret.q >> i);
-                       ret.q += ret.i >> i;
-                       ret.i = tmp;
-                       angle += arctg[i];
-               } else {
-                       tmp = ret.i + (ret.q >> i);
-                       ret.q -= ret.i >> i;
-                       ret.i = tmp;
-                       angle -= arctg[i];
-               }
-       }
-
-       ret.i *= signx;
-       ret.q *= signx;
-
-       return ret;
-}
index 57a1ad8..4213cac 100644 (file)
@@ -7,13 +7,6 @@
 
 struct b43_wldev;
 
-/* Complex number using 2 32-bit signed integers */
-struct b43_c32 { s32 i, q; };
-
-#define CORDIC_CONVERT(value)  (((value) >= 0) ? \
-                                ((((value) >> 15) + 1) >> 1) : \
-                                -((((-(value)) >> 15) + 1) >> 1))
-
 /* PHY register routing bits */
 #define B43_PHYROUTE                   0x0C00 /* PHY register routing bits mask */
 #define  B43_PHYROUTE_BASE             0x0000 /* Base registers */
@@ -450,6 +443,4 @@ bool b43_is_40mhz(struct b43_wldev *dev);
 
 void b43_phy_force_clock(struct b43_wldev *dev, bool force);
 
-struct b43_c32 b43_cordic(int theta);
-
 #endif /* LINUX_B43_PHY_COMMON_H_ */
index 6922cbb..46408a5 100644 (file)
@@ -23,6 +23,7 @@
 
 */
 
+#include <linux/cordic.h>
 #include <linux/slab.h>
 
 #include "b43.h"
@@ -1780,9 +1781,9 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
 {
        struct b43_phy_lp *lpphy = dev->phy.lp;
        u16 buf[64];
-       int i, samples = 0, angle = 0;
+       int i, samples = 0, theta = 0;
        int rotation = (((36 * freq) / 20) << 16) / 100;
-       struct b43_c32 sample;
+       struct cordic_iq sample;
 
        lpphy->tx_tone_freq = freq;
 
@@ -1798,10 +1799,10 @@ static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
        }
 
        for (i = 0; i < samples; i++) {
-               sample = b43_cordic(angle);
-               angle += rotation;
-               buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8;
-               buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF);
+               sample = cordic_calc_iq(CORDIC_FIXED(theta));
+               theta += rotation;
+               buf[i] = CORDIC_FLOAT((sample.i * max) & 0xFF) << 8;
+               buf[i] |= CORDIC_FLOAT((sample.q * max) & 0xFF);
        }
 
        b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);
index 44ab080..7d30036 100644 (file)
@@ -23,6 +23,7 @@
 
 */
 
+#include <linux/cordic.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/types.h>
@@ -1513,7 +1514,7 @@ static void b43_radio_init2055(struct b43_wldev *dev)
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
 static int b43_nphy_load_samples(struct b43_wldev *dev,
-                                       struct b43_c32 *samples, u16 len) {
+                                       struct cordic_iq *samples, u16 len) {
        struct b43_phy_n *nphy = dev->phy.n;
        u16 i;
        u32 *data;
@@ -1544,7 +1545,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
 {
        int i;
        u16 bw, len, rot, angle;
-       struct b43_c32 *samples;
+       struct cordic_iq *samples;
 
        bw = b43_is_40mhz(dev) ? 40 : 20;
        len = bw << 3;
@@ -1561,7 +1562,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                len = bw << 1;
        }
 
-       samples = kcalloc(len, sizeof(struct b43_c32), GFP_KERNEL);
+       samples = kcalloc(len, sizeof(struct cordic_iq), GFP_KERNEL);
        if (!samples) {
                b43err(dev->wl, "allocation for samples generation failed\n");
                return 0;
@@ -1570,10 +1571,10 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
        angle = 0;
 
        for (i = 0; i < len; i++) {
-               samples[i] = b43_cordic(angle);
+               samples[i] = cordic_calc_iq(CORDIC_FIXED(angle));
                angle += rot;
-               samples[i].q = CORDIC_CONVERT(samples[i].q * max);
-               samples[i].i = CORDIC_CONVERT(samples[i].i * max);
+               samples[i].q = CORDIC_FLOAT(samples[i].q * max);
+               samples[i].i = CORDIC_FLOAT(samples[i].i * max);
        }
 
        i = b43_nphy_load_samples(dev, samples, len);
index 1f5a9b9..22fd95a 100644 (file)
@@ -54,3 +54,5 @@ brcmfmac-$(CONFIG_BRCM_TRACING) += \
                tracepoint.o
 brcmfmac-$(CONFIG_OF) += \
                of.o
+brcmfmac-$(CONFIG_DMI) += \
+               dmi.o
index 3e37c8c..b2ad212 100644 (file)
@@ -342,6 +342,37 @@ static int brcmf_sdiod_skbuff_write(struct brcmf_sdio_dev *sdiodev,
        return err;
 }
 
+static int mmc_submit_one(struct mmc_data *md, struct mmc_request *mr,
+                         struct mmc_command *mc, int sg_cnt, int req_sz,
+                         int func_blk_sz, u32 *addr,
+                         struct brcmf_sdio_dev *sdiodev,
+                         struct sdio_func *func, int write)
+{
+       int ret;
+
+       md->sg_len = sg_cnt;
+       md->blocks = req_sz / func_blk_sz;
+       mc->arg |= (*addr & 0x1FFFF) << 9;      /* address */
+       mc->arg |= md->blocks & 0x1FF;  /* block count */
+       /* incrementing addr for function 1 */
+       if (func->num == 1)
+               *addr += req_sz;
+
+       mmc_set_data_timeout(md, func->card);
+       mmc_wait_for_req(func->card->host, mr);
+
+       ret = mc->error ? mc->error : md->error;
+       if (ret == -ENOMEDIUM) {
+               brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
+       } else if (ret != 0) {
+               brcmf_err("CMD53 sg block %s failed %d\n",
+                         write ? "write" : "read", ret);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
 /**
  * brcmf_sdiod_sglist_rw - SDIO interface function for block data access
  * @sdiodev: brcmfmac sdio device
@@ -360,11 +391,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
                                 struct sk_buff_head *pktlist)
 {
        unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
-       unsigned int max_req_sz, orig_offset, dst_offset;
-       unsigned short max_seg_cnt, seg_sz;
+       unsigned int max_req_sz, src_offset, dst_offset;
        unsigned char *pkt_data, *orig_data, *dst_data;
-       struct sk_buff *pkt_next = NULL, *local_pkt_next;
        struct sk_buff_head local_list, *target_list;
+       struct sk_buff *pkt_next = NULL, *src;
+       unsigned short max_seg_cnt;
        struct mmc_request mmc_req;
        struct mmc_command mmc_cmd;
        struct mmc_data mmc_dat;
@@ -404,9 +435,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
        max_req_sz = sdiodev->max_request_size;
        max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
                            target_list->qlen);
-       seg_sz = target_list->qlen;
-       pkt_offset = 0;
-       pkt_next = target_list->next;
 
        memset(&mmc_req, 0, sizeof(struct mmc_request));
        memset(&mmc_cmd, 0, sizeof(struct mmc_command));
@@ -425,12 +453,12 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
        mmc_req.cmd = &mmc_cmd;
        mmc_req.data = &mmc_dat;
 
-       while (seg_sz) {
-               req_sz = 0;
-               sg_cnt = 0;
-               sgl = sdiodev->sgtable.sgl;
-               /* prep sg table */
-               while (pkt_next != (struct sk_buff *)target_list) {
+       req_sz = 0;
+       sg_cnt = 0;
+       sgl = sdiodev->sgtable.sgl;
+       skb_queue_walk(target_list, pkt_next) {
+               pkt_offset = 0;
+               while (pkt_offset < pkt_next->len) {
                        pkt_data = pkt_next->data + pkt_offset;
                        sg_data_sz = pkt_next->len - pkt_offset;
                        if (sg_data_sz > sdiodev->max_segment_size)
@@ -439,72 +467,55 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
                                sg_data_sz = max_req_sz - req_sz;
 
                        sg_set_buf(sgl, pkt_data, sg_data_sz);
-
                        sg_cnt++;
+
                        sgl = sg_next(sgl);
                        req_sz += sg_data_sz;
                        pkt_offset += sg_data_sz;
-                       if (pkt_offset == pkt_next->len) {
-                               pkt_offset = 0;
-                               pkt_next = pkt_next->next;
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) {
+                               ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd,
+                                                    sg_cnt, req_sz, func_blk_sz,
+                                                    &addr, sdiodev, func, write);
+                               if (ret)
+                                       goto exit_queue_walk;
+                               req_sz = 0;
+                               sg_cnt = 0;
+                               sgl = sdiodev->sgtable.sgl;
                        }
-
-                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt)
-                               break;
-               }
-               seg_sz -= sg_cnt;
-
-               if (req_sz % func_blk_sz != 0) {
-                       brcmf_err("sg request length %u is not %u aligned\n",
-                                 req_sz, func_blk_sz);
-                       ret = -ENOTBLK;
-                       goto exit;
-               }
-
-               mmc_dat.sg_len = sg_cnt;
-               mmc_dat.blocks = req_sz / func_blk_sz;
-               mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
-               mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
-               /* incrementing addr for function 1 */
-               if (func->num == 1)
-                       addr += req_sz;
-
-               mmc_set_data_timeout(&mmc_dat, func->card);
-               mmc_wait_for_req(func->card->host, &mmc_req);
-
-               ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
-               if (ret == -ENOMEDIUM) {
-                       brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
-                       break;
-               } else if (ret != 0) {
-                       brcmf_err("CMD53 sg block %s failed %d\n",
-                                 write ? "write" : "read", ret);
-                       ret = -EIO;
-                       break;
                }
        }
-
+       if (sg_cnt)
+               ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd,
+                                    sg_cnt, req_sz, func_blk_sz,
+                                    &addr, sdiodev, func, write);
+exit_queue_walk:
        if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
-               local_pkt_next = local_list.next;
-               orig_offset = 0;
+               src = __skb_peek(&local_list);
+               src_offset = 0;
                skb_queue_walk(pktlist, pkt_next) {
                        dst_offset = 0;
-                       do {
-                               req_sz = local_pkt_next->len - orig_offset;
-                               req_sz = min_t(uint, pkt_next->len - dst_offset,
-                                              req_sz);
-                               orig_data = local_pkt_next->data + orig_offset;
+
+                       /* This is safe because we must have enough SKB data
+                        * in the local list to cover everything in pktlist.
+                        */
+                       while (1) {
+                               req_sz = pkt_next->len - dst_offset;
+                               if (req_sz > src->len - src_offset)
+                                       req_sz = src->len - src_offset;
+
+                               orig_data = src->data + src_offset;
                                dst_data = pkt_next->data + dst_offset;
                                memcpy(dst_data, orig_data, req_sz);
-                               orig_offset += req_sz;
-                               dst_offset += req_sz;
-                               if (orig_offset == local_pkt_next->len) {
-                                       orig_offset = 0;
-                                       local_pkt_next = local_pkt_next->next;
+
+                               src_offset += req_sz;
+                               if (src_offset == src->len) {
+                                       src_offset = 0;
+                                       src = skb_peek_next(src, &local_list);
                                }
+                               dst_offset += req_sz;
                                if (dst_offset == pkt_next->len)
                                        break;
-                       } while (!skb_queue_empty(&local_list));
+                       }
                }
        }
 
index 94044a7..1f1e95a 100644 (file)
@@ -214,7 +214,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
                                       sizeof(ifp->mac_addr));
        if (err < 0) {
-               brcmf_err("Retreiving cur_etheraddr failed, %d\n", err);
+               brcmf_err("Retrieving cur_etheraddr failed, %d\n", err);
                goto done;
        }
        memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN);
@@ -269,7 +269,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        strcpy(buf, "ver");
        err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
        if (err < 0) {
-               brcmf_err("Retreiving version information failed, %d\n",
+               brcmf_err("Retrieving version information failed, %d\n",
                          err);
                goto done;
        }
@@ -448,7 +448,8 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
                }
        }
        if (!found) {
-               /* No platform data for this device, try OF (Open Firwmare) */
+               /* No platform data for this device, try OF and DMI data */
+               brcmf_dmi_probe(settings, chip, chiprev);
                brcmf_of_probe(dev, bus_type, settings);
        }
        return settings;
index a34642c..4ce56be 100644 (file)
@@ -59,6 +59,7 @@ struct brcmf_mp_device {
        bool            iapp;
        bool            ignore_probe_fail;
        struct brcmfmac_pd_cc *country_codes;
+       const char      *board_type;
        union {
                struct brcmfmac_sdio_pd sdio;
        } bus;
@@ -74,4 +75,11 @@ void brcmf_release_module_param(struct brcmf_mp_device *module_param);
 /* Sets dongle media info (drv_version, mac address). */
 int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
 
+#ifdef CONFIG_DMI
+void brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev);
+#else
+static inline void
+brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev) {}
+#endif
+
 #endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
new file mode 100644 (file)
index 0000000..51d76ac
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 Hans de Goede <hdegoede@redhat.com>
+ *
+ * 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/dmi.h>
+#include <linux/mod_devicetable.h>
+#include "core.h"
+#include "common.h"
+#include "brcm_hw_ids.h"
+
+/* The DMI data never changes so we can use a static buf for this */
+static char dmi_board_type[128];
+
+struct brcmf_dmi_data {
+       u32 chip;
+       u32 chiprev;
+       const char *board_type;
+};
+
+/* NOTE: Please keep all entries sorted alphabetically */
+
+static const struct brcmf_dmi_data gpd_win_pocket_data = {
+       BRCM_CC_4356_CHIP_ID, 2, "gpd-win-pocket"
+};
+
+static const struct brcmf_dmi_data jumper_ezpad_mini3_data = {
+       BRCM_CC_43430_CHIP_ID, 0, "jumper-ezpad-mini3"
+};
+
+static const struct brcmf_dmi_data meegopad_t08_data = {
+       BRCM_CC_43340_CHIP_ID, 2, "meegopad-t08"
+};
+
+static const struct dmi_system_id dmi_platform_data[] = {
+       {
+               /* Match for the GPDwin which unfortunately uses somewhat
+                * generic dmi strings, which is why we test for 4 strings.
+                * Comparing against 23 other byt/cht boards, board_vendor
+                * and board_name are unique to the GPDwin, where as only one
+                * other board has the same board_serial and 3 others have
+                * the same default product_name. Also the GPDwin is the
+                * only device to have both board_ and product_name not set.
+                */
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Default string"),
+                       DMI_MATCH(DMI_BOARD_SERIAL, "Default string"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+               },
+               .driver_data = (void *)&gpd_win_pocket_data,
+       },
+       {
+               /* Jumper EZpad mini3 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+                       /* jumperx.T87.KFBNEEA02 with the version-nr dropped */
+                       DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
+               },
+               .driver_data = (void *)&jumper_ezpad_mini3_data,
+       },
+       {
+               /* Meegopad T08 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+                       DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+               },
+               .driver_data = (void *)&meegopad_t08_data,
+       },
+       {}
+};
+
+void brcmf_dmi_probe(struct brcmf_mp_device *settings, u32 chip, u32 chiprev)
+{
+       const struct dmi_system_id *match;
+       const struct brcmf_dmi_data *data;
+       const char *sys_vendor;
+       const char *product_name;
+
+       /* Some models have DMI strings which are too generic, e.g.
+        * "Default string", we use a quirk table for these.
+        */
+       for (match = dmi_first_match(dmi_platform_data);
+            match;
+            match = dmi_first_match(match + 1)) {
+               data = match->driver_data;
+
+               if (data->chip == chip && data->chiprev == chiprev) {
+                       settings->board_type = data->board_type;
+                       return;
+               }
+       }
+
+       /* Not found in the quirk-table, use sys_vendor-product_name */
+       sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+       product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+       if (sys_vendor && product_name) {
+               snprintf(dmi_board_type, sizeof(dmi_board_type), "%s-%s",
+                        sys_vendor, product_name);
+               settings->board_type = dmi_board_type;
+       }
+}
index 9095b83..dad3c1c 100644 (file)
@@ -14,6 +14,7 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/efi.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/device.h>
@@ -445,6 +446,75 @@ struct brcmf_fw {
 
 static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
 
+#ifdef CONFIG_EFI
+/* In some cases the EFI-var stored nvram contains "ccode=ALL" or "ccode=XV"
+ * to specify "worldwide" compatible settings, but these 2 ccode-s do not work
+ * properly. "ccode=ALL" causes channels 12 and 13 to not be available,
+ * "ccode=XV" causes all 5GHz channels to not be available. So we replace both
+ * with "ccode=X2" which allows channels 12+13 and 5Ghz channels in
+ * no-Initiate-Radiation mode. This means that we will never send on these
+ * channels without first having received valid wifi traffic on the channel.
+ */
+static void brcmf_fw_fix_efi_nvram_ccode(char *data, unsigned long data_len)
+{
+       char *ccode;
+
+       ccode = strnstr((char *)data, "ccode=ALL", data_len);
+       if (!ccode)
+               ccode = strnstr((char *)data, "ccode=XV\r", data_len);
+       if (!ccode)
+               return;
+
+       ccode[6] = 'X';
+       ccode[7] = '2';
+       ccode[8] = '\r';
+}
+
+static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
+{
+       const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
+       struct efivar_entry *nvram_efivar;
+       unsigned long data_len = 0;
+       u8 *data = NULL;
+       int err;
+
+       nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
+       if (!nvram_efivar)
+               return NULL;
+
+       memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
+       nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
+                                               0xb5, 0x1f, 0x43, 0x26,
+                                               0x81, 0x23, 0xd1, 0x13);
+
+       err = efivar_entry_size(nvram_efivar, &data_len);
+       if (err)
+               goto fail;
+
+       data = kmalloc(data_len, GFP_KERNEL);
+       if (!data)
+               goto fail;
+
+       err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
+       if (err)
+               goto fail;
+
+       brcmf_fw_fix_efi_nvram_ccode(data, data_len);
+       brcmf_info("Using nvram EFI variable\n");
+
+       kfree(nvram_efivar);
+       *data_len_ret = data_len;
+       return data;
+
+fail:
+       kfree(data);
+       kfree(nvram_efivar);
+       return NULL;
+}
+#else
+static u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; }
+#endif
+
 static void brcmf_fw_free_request(struct brcmf_fw_request *req)
 {
        struct brcmf_fw_item *item;
@@ -463,11 +533,12 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
 {
        struct brcmf_fw *fwctx = ctx;
        struct brcmf_fw_item *cur;
+       bool free_bcm47xx_nvram = false;
+       bool kfree_nvram = false;
        u32 nvram_length = 0;
        void *nvram = NULL;
        u8 *data = NULL;
        size_t data_len;
-       bool raw_nvram;
 
        brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
 
@@ -476,12 +547,13 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
        if (fw && fw->data) {
                data = (u8 *)fw->data;
                data_len = fw->size;
-               raw_nvram = false;
        } else {
-               data = bcm47xx_nvram_get_contents(&data_len);
-               if (!data && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
+               if ((data = bcm47xx_nvram_get_contents(&data_len)))
+                       free_bcm47xx_nvram = true;
+               else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
+                       kfree_nvram = true;
+               else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                        goto fail;
-               raw_nvram = true;
        }
 
        if (data)
@@ -489,8 +561,11 @@ static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
                                             fwctx->req->domain_nr,
                                             fwctx->req->bus_nr);
 
-       if (raw_nvram)
+       if (free_bcm47xx_nvram)
                bcm47xx_nvram_release_contents(data);
+       if (kfree_nvram)
+               kfree(data);
+
        release_firmware(fw);
        if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
                goto fail;
@@ -504,90 +579,75 @@ fail:
        return -ENOENT;
 }
 
-static int brcmf_fw_request_next_item(struct brcmf_fw *fwctx, bool async)
+static int brcmf_fw_complete_request(const struct firmware *fw,
+                                    struct brcmf_fw *fwctx)
 {
-       struct brcmf_fw_item *cur;
-       const struct firmware *fw = NULL;
-       int ret;
-
-       cur = &fwctx->req->items[fwctx->curpos];
-
-       brcmf_dbg(TRACE, "%srequest for %s\n", async ? "async " : "",
-                 cur->path);
-
-       if (async)
-               ret = request_firmware_nowait(THIS_MODULE, true, cur->path,
-                                             fwctx->dev, GFP_KERNEL, fwctx,
-                                             brcmf_fw_request_done);
-       else
-               ret = request_firmware(&fw, cur->path, fwctx->dev);
-
-       if (ret < 0) {
-               brcmf_fw_request_done(NULL, fwctx);
-       } else if (!async && fw) {
-               brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path,
-                         fw ? "" : "not ");
-               if (cur->type == BRCMF_FW_TYPE_BINARY)
-                       cur->binary = fw;
-               else if (cur->type == BRCMF_FW_TYPE_NVRAM)
-                       brcmf_fw_request_nvram_done(fw, fwctx);
-               else
-                       release_firmware(fw);
-
-               return -EAGAIN;
-       }
-       return 0;
-}
-
-static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
-{
-       struct brcmf_fw *fwctx = ctx;
-       struct brcmf_fw_item *cur;
+       struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
        int ret = 0;
 
-       cur = &fwctx->req->items[fwctx->curpos];
-
-       brcmf_dbg(TRACE, "enter: firmware %s %sfound\n", cur->path,
-                 fw ? "" : "not ");
-
-       if (!fw)
-               ret = -ENOENT;
+       brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path, fw ? "" : "not ");
 
        switch (cur->type) {
        case BRCMF_FW_TYPE_NVRAM:
                ret = brcmf_fw_request_nvram_done(fw, fwctx);
                break;
        case BRCMF_FW_TYPE_BINARY:
-               cur->binary = fw;
+               if (fw)
+                       cur->binary = fw;
+               else
+                       ret = -ENOENT;
                break;
        default:
                /* something fishy here so bail out early */
                brcmf_err("unknown fw type: %d\n", cur->type);
                release_firmware(fw);
                ret = -EINVAL;
-               goto fail;
        }
 
-       if (ret < 0 && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
-               goto fail;
+       return (cur->flags & BRCMF_FW_REQF_OPTIONAL) ? 0 : ret;
+}
 
-       do {
-               if (++fwctx->curpos == fwctx->req->n_items) {
-                       ret = 0;
-                       goto done;
-               }
+static int brcmf_fw_request_firmware(const struct firmware **fw,
+                                    struct brcmf_fw *fwctx)
+{
+       struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
+       int ret;
 
-               ret = brcmf_fw_request_next_item(fwctx, false);
-       } while (ret == -EAGAIN);
+       /* nvram files are board-specific, first try a board-specific path */
+       if (cur->type == BRCMF_FW_TYPE_NVRAM && fwctx->req->board_type) {
+               char alt_path[BRCMF_FW_NAME_LEN];
 
-       return;
+               strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN);
+               /* strip .txt at the end */
+               alt_path[strlen(alt_path) - 4] = 0;
+               strlcat(alt_path, ".", BRCMF_FW_NAME_LEN);
+               strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN);
+               strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN);
 
-fail:
-       brcmf_dbg(TRACE, "failed err=%d: dev=%s, fw=%s\n", ret,
-                 dev_name(fwctx->dev), cur->path);
-       brcmf_fw_free_request(fwctx->req);
-       fwctx->req = NULL;
-done:
+               ret = request_firmware(fw, alt_path, fwctx->dev);
+               if (ret == 0)
+                       return ret;
+       }
+
+       return request_firmware(fw, cur->path, fwctx->dev);
+}
+
+static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       int ret;
+
+       ret = brcmf_fw_complete_request(fw, fwctx);
+
+       while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) {
+               brcmf_fw_request_firmware(&fw, fwctx);
+               ret = brcmf_fw_complete_request(fw, ctx);
+       }
+
+       if (ret) {
+               brcmf_fw_free_request(fwctx->req);
+               fwctx->req = NULL;
+       }
        fwctx->done(fwctx->dev, ret, fwctx->req);
        kfree(fwctx);
 }
@@ -611,7 +671,9 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
                           void (*fw_cb)(struct device *dev, int err,
                                         struct brcmf_fw_request *req))
 {
+       struct brcmf_fw_item *first = &req->items[0];
        struct brcmf_fw *fwctx;
+       int ret;
 
        brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
        if (!fw_cb)
@@ -628,7 +690,12 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
        fwctx->req = req;
        fwctx->done = fw_cb;
 
-       brcmf_fw_request_next_item(fwctx, true);
+       ret = request_firmware_nowait(THIS_MODULE, true, first->path,
+                                     fwctx->dev, GFP_KERNEL, fwctx,
+                                     brcmf_fw_request_done);
+       if (ret < 0)
+               brcmf_fw_request_done(NULL, fwctx);
+
        return 0;
 }
 
@@ -641,8 +708,9 @@ brcmf_fw_alloc_request(u32 chip, u32 chiprev,
        struct brcmf_fw_request *fwreq;
        char chipname[12];
        const char *mp_path;
+       size_t mp_path_len;
        u32 i, j;
-       char end;
+       char end = '\0';
        size_t reqsz;
 
        for (i = 0; i < table_size; i++) {
@@ -667,7 +735,10 @@ brcmf_fw_alloc_request(u32 chip, u32 chiprev,
                   mapping_table[i].fw_base, chipname);
 
        mp_path = brcmf_mp_global.firmware_path;
-       end = mp_path[strlen(mp_path) - 1];
+       mp_path_len = strnlen(mp_path, BRCMF_FW_ALTPATH_LEN);
+       if (mp_path_len)
+               end = mp_path[mp_path_len - 1];
+
        fwreq->n_items = n_fwnames;
 
        for (j = 0; j < n_fwnames; j++) {
index 2893e56..a0834be 100644 (file)
@@ -70,6 +70,7 @@ struct brcmf_fw_request {
        u16 domain_nr;
        u16 bus_nr;
        u32 n_items;
+       const char *board_type;
        struct brcmf_fw_item items[0];
 };
 
index d5bb81e..39ac1bb 100644 (file)
 
 #define BRCMF_VHT_CAP_MCS_MAP_NSS_MAX  8
 
+#define BRCMF_HE_CAP_MCS_MAP_NSS_MAX   8
+
 /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
  * ioctl. It is relatively small because firmware has small maximum size input
  * playload restriction for ioctls.
@@ -601,13 +603,37 @@ struct brcmf_sta_info_le {
        __le32 rx_pkts_retried;        /* # rx with retry bit set */
        __le32 tx_rate_fallback;       /* lowest fallback TX rate */
 
-       /* Fields valid for ver >= 5 */
-       struct {
-               __le32 count;                                   /* # rates in this set */
-               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
-               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
-               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
-       } rateset_adv;
+       union {
+               struct {
+                       struct {
+                               __le32 count;                                   /* # rates in this set */
+                               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
+                               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
+                               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
+                       } rateset_adv;
+               } v5;
+
+               struct {
+                       __le32 rx_dur_total;    /* total user RX duration (estimated) */
+                       __le16 chanspec;        /** chanspec this sta is on */
+                       __le16 pad_1;
+                       struct {
+                               __le16 version;                                 /* version */
+                               __le16 len;                                     /* length */
+                               __le32 count;                                   /* # rates in this set */
+                               u8 rates[BRCMF_MAXRATES_IN_SET];                /* rates in 500kbps units w/hi bit set if basic */
+                               u8 mcs[BRCMF_MCSSET_LEN];                       /* supported mcs index bit map */
+                               __le16 vht_mcs[BRCMF_VHT_CAP_MCS_MAP_NSS_MAX];  /* supported mcs index bit map per nss */
+                               __le16 he_mcs[BRCMF_HE_CAP_MCS_MAP_NSS_MAX];    /* supported he mcs index bit map per nss */
+                       } rateset_adv;          /* rateset along with mcs index bitmap */
+                       __le16 wpauth;          /* authentication type */
+                       u8 algo;                /* crypto algorithm */
+                       u8 pad_2;
+                       __le32 tx_rspec;        /* Rate of last successful tx frame */
+                       __le32 rx_rspec;        /* Rate of last successful rx frame */
+                       __le32 wnm_cap;         /* wnm capabilities */
+               } v7;
+       };
 };
 
 struct brcmf_chanspec_list {
index aee6e59..84e3373 100644 (file)
@@ -27,11 +27,20 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
                    struct brcmf_mp_device *settings)
 {
        struct brcmfmac_sdio_pd *sdio = &settings->bus.sdio;
-       struct device_node *np = dev->of_node;
+       struct device_node *root, *np = dev->of_node;
+       struct property *prop;
        int irq;
        u32 irqf;
        u32 val;
 
+       /* Set board-type to the first string of the machine compatible prop */
+       root = of_find_node_by_path("/");
+       if (root) {
+               prop = of_find_property(root, "compatible", NULL);
+               settings->board_type = of_prop_next_string(prop, NULL);
+               of_node_put(root);
+       }
+
        if (!np || bus_type != BRCMF_BUSTYPE_SDIO ||
            !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
                return;
index 5dea569..956a8b2 100644 (file)
@@ -1785,6 +1785,7 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo)
        fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
        fwreq->items[BRCMF_PCIE_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
        fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL;
+       fwreq->board_type = devinfo->settings->board_type;
        /* NVRAM reserves PCI domain 0 for Broadcom's SDK faked bus */
        fwreq->domain_nr = pci_domain_nr(devinfo->pdev->bus) + 1;
        fwreq->bus_nr = devinfo->pdev->bus->number;
index b2e1ab5..22646a3 100644 (file)
@@ -4174,6 +4174,7 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus)
 
        fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY;
        fwreq->items[BRCMF_SDIO_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM;
+       fwreq->board_type = bus->sdiodev->settings->board_type;
 
        return fwreq;
 }
index 81ff558..6188275 100644 (file)
@@ -846,8 +846,8 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                status = brcms_c_aggregatable(wl->wlc, tid);
                spin_unlock_bh(&wl->lock);
                if (!status) {
-                       brcms_err(wl->wlc->hw->d11core,
-                                 "START: tid %d is not agg\'able\n", tid);
+                       brcms_dbg_ht(wl->wlc->hw->d11core,
+                                    "START: tid %d is not agg\'able\n", tid);
                        return -EINVAL;
                }
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
index 4960f7d..e9e8337 100644 (file)
@@ -220,13 +220,6 @@ enum phy_cal_mode {
 #define BB_MULT_MASK           0x0000ffff
 #define BB_MULT_VALID_MASK     0x80000000
 
-#define CORDIC_AG      39797
-#define        CORDIC_NI       18
-#define        FIXED(X)        ((s32)((X) << 16))
-
-#define        FLOAT(X) \
-       (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1))
-
 #define PHY_CHAIN_TX_DISABLE_TEMP      115
 #define PHY_HYSTERESIS_DELTATEMP       5
 
index 9fb0d9f..e78a93a 100644 (file)
@@ -3447,8 +3447,8 @@ wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val,
 
                theta += rot;
 
-               i_samp = (u16) (FLOAT(tone_samp.i * max_val) & 0x3ff);
-               q_samp = (u16) (FLOAT(tone_samp.q * max_val) & 0x3ff);
+               i_samp = (u16)(CORDIC_FLOAT(tone_samp.i * max_val) & 0x3ff);
+               q_samp = (u16)(CORDIC_FLOAT(tone_samp.q * max_val) & 0x3ff);
                data_buf[t] = (i_samp << 10) | q_samp;
        }
 
index a57f271..f4f5e90 100644 (file)
@@ -23089,8 +23089,8 @@ wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val,
 
                theta += rot;
 
-               tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val);
-               tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val);
+               tone_buf[t].q = (s32)CORDIC_FLOAT(tone_buf[t].q * max_val);
+               tone_buf[t].i = (s32)CORDIC_FLOAT(tone_buf[t].i * max_val);
        }
 
        wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps);
index eb5db94..8ac3482 100644 (file)
@@ -128,7 +128,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
                }
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 
@@ -140,7 +140,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
                ch->band = BRCMU_CHAN_BAND_2G;
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 }
@@ -167,7 +167,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->sb = BRCMU_CHAN_SB_U;
                        ch->control_ch_num += CH_10MHZ_APART;
                } else {
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                }
                break;
        case BRCMU_CHSPEC_D11AC_BW_80:
@@ -188,7 +188,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->control_ch_num += CH_30MHZ_APART;
                        break;
                default:
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                        break;
                }
                break;
@@ -222,13 +222,13 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                        ch->control_ch_num += CH_70MHZ_APART;
                        break;
                default:
-                       WARN_ON_ONCE(1);
+                       WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                        break;
                }
                break;
        case BRCMU_CHSPEC_D11AC_BW_8080:
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 
@@ -240,7 +240,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                ch->band = BRCMU_CHAN_BAND_2G;
                break;
        default:
-               WARN_ON_ONCE(1);
+               WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
                break;
        }
 }
index 04dd7a9..5512c7f 100644 (file)
@@ -5462,7 +5462,7 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) {
            we have to add a spin lock... */
        rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
        while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
-               ptr += sprintf(ptr, "%pM %*s rssi = %d",
+               ptr += sprintf(ptr, "%pM %.*s rssi = %d",
                               BSSList_rid.bssid,
                                (int)BSSList_rid.ssidLen,
                                BSSList_rid.ssid,
index d6ec44d..5623955 100644 (file)
@@ -15,9 +15,9 @@ config IPW2100
           A driver for the Intel PRO/Wireless 2100 Network 
          Connection 802.11b wireless network adapter.
 
-          See <file:Documentation/networking/README.ipw2100> for information on
-          the capabilities currently enabled in this driver and for tips
-          for debugging issues and problems.
+          See <file:Documentation/networking/device_drivers/intel/ipw2100.txt>
+         for information on the capabilities currently enabled in this driver
+         and for tips for debugging issues and problems.
 
          In order to use this driver, you will need a firmware image for it.
           You can obtain the firmware from
@@ -77,8 +77,8 @@ config IPW2200
           A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
          Connection adapters. 
 
-          See <file:Documentation/networking/README.ipw2200> for 
-         information on the capabilities currently enabled in this 
+          See <file:Documentation/networking/device_drivers/intel/ipw2200.txt>
+         for information on the capabilities currently enabled in this
          driver and for tips for debugging issues and problems.
 
          In order to use this driver, you will need a firmware image for it.
index 910db46..6e31687 100644 (file)
@@ -8370,7 +8370,7 @@ static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
        if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
                printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
                       "(detected version id of %u). "
-                      "See Documentation/networking/README.ipw2100\n",
+                      "See Documentation/networking/device_drivers/intel/ipw2100.txt\n",
                       h->version);
                return 1;
        }
index e8983c6..a697edd 100644 (file)
@@ -781,7 +781,7 @@ il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta,
 
        switch (scale_action) {
        case -1:
-               /* Decrese rate */
+               /* Decrease rate */
                if (low != RATE_INVALID)
                        idx = low;
                break;
index 280cd8a..6b4488a 100644 (file)
@@ -559,7 +559,7 @@ il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
                        decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK;
                        break;
                }
-               /* fall through if TTAK OK */
+               /* fall through if TTAK OK */
        default:
                if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK))
                        decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC;
index 6514baf..a2f86cb 100644 (file)
@@ -2695,6 +2695,7 @@ il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
                    RX_RES_STATUS_BAD_KEY_TTAK)
                        break;
+               /* fall through */
 
        case RX_RES_STATUS_SEC_TYPE_WEP:
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
@@ -2704,6 +2705,7 @@ il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr,
                        D_RX("Packet destroyed\n");
                        return -1;
                }
+               /* fall through */
        case RX_RES_STATUS_SEC_TYPE_CCMP:
                if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
                    RX_RES_STATUS_DECRYPT_OK) {
index 04e376c..ff41987 100644 (file)
@@ -11,6 +11,7 @@ iwlwifi-objs          += pcie/ctxt-info.o pcie/ctxt-info-gen3.o
 iwlwifi-objs           += pcie/trans-gen2.o pcie/tx-gen2.o
 iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o
 iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o
+iwlwifi-objs           += iwl-dbg-tlv.o
 iwlwifi-objs           += iwl-trans.o
 iwlwifi-objs           += fw/notif-wait.o
 iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o
index da5d5f9..8b23391 100644 (file)
@@ -323,7 +323,6 @@ MODULE_FIRMWARE(IWL_22000_HR_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_JF_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_A_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_B_F0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
-MODULE_FIRMWARE(IWL_22000_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_B_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_JF_B0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
 MODULE_FIRMWARE(IWL_22000_HR_A0_QNJ_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
new file mode 100644 (file)
index 0000000..ab82b7a
--- /dev/null
@@ -0,0 +1,401 @@
+/******************************************************************************
+ *
+ * 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) 2018 Intel 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __iwl_fw_dbg_tlv_h__
+#define __iwl_fw_dbg_tlv_h__
+
+#include <linux/bitops.h>
+
+/*
+ * struct iwl_fw_ini_header: Common Header for all debug group TLV's structures
+ * @tlv_version: version info
+ * @apply_point: &enum iwl_fw_ini_apply_point
+ * @data: TLV data followed
+ **/
+struct iwl_fw_ini_header {
+       __le32 tlv_version;
+       __le32 apply_point;
+       u8 data[];
+} __packed; /* FW_INI_HEADER_TLV_S */
+
+/**
+ * struct iwl_fw_ini_allocation_tlv - (IWL_FW_INI_TLV_TYPE_BUFFER_ALLOCATION)
+ * buffer allocation TLV - for debug
+ *
+ * @iwl_fw_ini_header: header
+ * @allocation_id: &enum iwl_fw_ini_allocation_id - to bind allocation and hcmd
+ *     if needed (DBGC1/DBGC2/SDFX/...)
+ * @buffer_location: type of iwl_fw_ini_buffer_location
+ * @size: size in bytes
+ * @max_fragments: the maximum allowed fragmentation in the desired memory
+ *     allocation above
+ * @min_frag_size: the minimum allowed fragmentation size in bytes
+*/
+struct iwl_fw_ini_allocation_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 allocation_id;
+       __le32 buffer_location;
+       __le32 size;
+       __le32 max_fragments;
+       __le32 min_frag_size;
+} __packed; /* FW_INI_BUFFER_ALLOCATION_TLV_S_VER_1 */
+
+/**
+ * struct iwl_fw_ini_hcmd (IWL_FW_INI_TLV_TYPE_HCMD)
+ * Generic Host command pass through TLV
+ *
+ * @id: the debug configuration command type for instance: 0xf6 / 0xf5 / DHC
+ * @group: the desired cmd group
+ * @padding: all zeros for dword alignment
+ * @data: all of the relevant command (0xf6/0xf5) to be sent
+*/
+struct iwl_fw_ini_hcmd {
+       u8 id;
+       u8 group;
+       __le16 padding;
+       u8 data[0];
+} __packed; /* FW_INI_HCMD_S */
+
+/**
+ * struct iwl_fw_ini_hcmd_tlv
+ * @header: header
+ * @hcmd: a variable length host-command to be sent to apply the configuration.
+ */
+struct iwl_fw_ini_hcmd_tlv {
+       struct iwl_fw_ini_header header;
+       struct iwl_fw_ini_hcmd hcmd;
+} __packed; /* FW_INI_HCMD_TLV_S_VER_1 */
+
+/*
+ * struct iwl_fw_ini_debug_flow_tlv (IWL_FW_INI_TLV_TYPE_DEBUG_FLOW)
+ *
+ * @header: header
+ * @debug_flow_cfg: &enum iwl_fw_ini_debug_flow
+ */
+struct iwl_fw_ini_debug_flow_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 debug_flow_cfg;
+} __packed; /* FW_INI_DEBUG_FLOW_TLV_S_VER_1 */
+
+#define IWL_FW_INI_MAX_REGION_ID       20
+#define IWL_FW_INI_MAX_NAME            32
+/**
+ * struct iwl_fw_ini_region_cfg
+ * @region_id: ID of this dump configuration
+ * @region_type: &enum iwl_fw_ini_region_type
+ * @num_regions: amount of regions in the address array.
+ * @allocation_id: For DRAM type field substitutes for allocation_id.
+ * @name_len: name length
+ * @name: file name to use for this region
+ * @size: size of the data, in bytes.(unused for IWL_FW_INI_REGION_DRAM_BUFFER)
+ * @start_addr: array of addresses. (unused for IWL_FW_INI_REGION_DRAM_BUFFER)
+ */
+struct iwl_fw_ini_region_cfg {
+       __le32 region_id;
+       __le32 region_type;
+       __le32 name_len;
+       u8 name[IWL_FW_INI_MAX_NAME];
+       union {
+               __le32 num_regions;
+               __le32 allocation_id;
+       };
+       __le32 size;
+       __le32 start_addr[];
+} __packed; /* FW_INI_REGION_CONFIG_S */
+
+/**
+ * struct iwl_fw_ini_region_tlv - (IWL_FW_INI_TLV_TYPE_REGION_CFG)
+ * DUMP sections define IDs and triggers that use those IDs TLV
+ * @header: header
+ * @num_regions: how many different region section and IDs are coming next
+ * @iwl_fw_ini_dump dump_config: list of dump configurations
+ */
+struct iwl_fw_ini_region_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 num_regions;
+       struct iwl_fw_ini_region_cfg region_config[];
+} __packed; /* FW_INI_REGION_CFG_S */
+
+/**
+ * struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG)
+ * Region sections define IDs and triggers that use those IDs TLV
+ *
+ * @trigger_id: enum &iwl_fw_ini_tigger_id
+ * @ignore_default: override FW TLV with binary TLV
+ * @dump_delay: delay from trigger fire to dump, in usec
+ * @occurrences: max amount of times to be fired
+ * @ignore_consec: ignore consecutive triggers, in usec
+ * @force_restart: force FW restart
+ * @multi_dut: initiate debug dump data on several DUTs
+ * @trigger_data: generic data to be utilized per trigger
+ * @num_regions: number of dump regions defined for this trigger
+ * @data: region IDs
+ */
+struct iwl_fw_ini_trigger {
+       __le32 trigger_id;
+       __le32 ignore_default;
+       __le32 dump_delay;
+       __le32 occurrences;
+       __le32 ignore_consec;
+       __le32 force_restart;
+       __le32 multi_dut;
+       __le32 trigger_data;
+       __le32 num_regions;
+       __le32 data[];
+} __packed; /* FW_INI_TRIGGER_CONFIG_S */
+
+/**
+ * struct iwl_fw_ini_trigger_tlv - (IWL_FW_INI_TLV_TYPE_TRIGGERS_CFG)
+ * DUMP sections define IDs and triggers that use those IDs TLV
+ *
+ * @header: header
+ * @num_triggers: how many different triggers section and IDs are coming next
+ * @trigger_config: list of trigger configurations
+ */
+struct iwl_fw_ini_trigger_tlv {
+       struct iwl_fw_ini_header header;
+       __le32 num_triggers;
+       struct iwl_fw_ini_trigger trigger_config[];
+} __packed; /* FW_INI_TRIGGER_CFG_S */
+
+/**
+ * enum iwl_fw_ini_trigger_id
+ * @IWL_FW_TRIGGER_ID_FW_ASSERT: FW assert
+ * @IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG: TFD queue hang
+ * @IWL_FW_TRIGGER_ID_FW_HW_ERROR: HW assert
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR: FW error notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING: FW warning notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO: FW info notification
+ * @IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG: FW debug notification
+ * @IWL_FW_TRIGGER_ID_USER_TRIGGER: User trigger
+ * @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY: peer inactivity
+ * @FW_DEBUG_TLV_TRIGGER_ID_HOST_DID_INITIATED_EVENT: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED: TX latency
+ *     threshold was crossed
+ * @IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED: TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER: Deauth initiated by host
+ * @IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST: stop GO request
+ * @IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST: start GO request
+ * @IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST: join P2P group request
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_START: scan started event
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_SUBMITTED: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED: BAR frame was received
+ * @IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED: agg TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED: EAPOL TX failed
+ * @IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED: suspicious TX response
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT: received suspicious auth
+ * @IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE: roaming was completed
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED: fast assoc failed
+ * @IWL_FW_TRIGGER_ID_HOST_D3_START: D3 start
+ * @IWL_FW_TRIGGER_ID_HOST_D3_END: D3 end
+ * @IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS: missed beacon events
+ * @IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS: P2P missed beacon events
+ * @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES:  undefined
+ * @IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED: undefined
+ * @IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED: authentication / association
+ *     failed
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE: scan complete event
+ * @IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT: scan abort complete
+ * @IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE: nic alive message was received
+ * @IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE: CSA was completed
+ * @IWL_FW_TRIGGER_ID_NUM: number of trigger IDs
+ */
+enum iwl_fw_ini_trigger_id {
+       /* Errors triggers */
+       IWL_FW_TRIGGER_ID_FW_ASSERT                             = 1,
+       IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG                         = 2,
+       IWL_FW_TRIGGER_ID_FW_HW_ERROR                           = 3,
+       /* Generic triggers */
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR                      = 4,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING                    = 5,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO                       = 6,
+       IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG                      = 7,
+       /* User Trigger */
+       IWL_FW_TRIGGER_ID_USER_TRIGGER                          = 8,
+       /* Host triggers */
+       IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY           = 9,
+       IWL_FW_TRIGGER_ID_HOST_DID_INITIATED_EVENT              = 10,
+       IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED     = 11,
+       IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED        = 12,
+       IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER               = 13,
+       IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST                  = 14,
+       IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST                 = 15,
+       IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST               = 16,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_START                       = 17,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_SUBITTED                    = 18,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS                      = 19,
+       IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG                   = 20,
+       IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED                     = 21,
+       IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED    = 22,
+       IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED         = 23,
+       IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED       = 24,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT       = 25,
+       IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE                    = 26,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED           = 27,
+       IWL_FW_TRIGGER_ID_HOST_D3_START                         = 28,
+       IWL_FW_TRIGGER_ID_HOST_D3_END                           = 29,
+       IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS               = 30,
+       IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS        = 31,
+       IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES          = 32,
+       IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED       = 33,
+       IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED                = 34,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE                    = 35,
+       IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT                       = 36,
+       IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE                        = 37,
+       IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE          = 38,
+       IWL_FW_TRIGGER_ID_NUM,
+}; /* FW_INI_TRIGGER_ID_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_apply_point
+ * @IWL_FW_INI_APPLY_INVALID: invalid
+ * @IWL_FW_INI_APPLY_EARLY: pre loading FW
+ * @IWL_FW_INI_APPLY_AFTER_ALIVE: first cmd from host after alive
+ * @IWL_FW_INI_APPLY_POST_INIT: last cmd in initialization sequence
+ * @IWL_FW_INI_APPLY_MISSED_BEACONS: missed beacons notification
+ * @IWL_FW_INI_APPLY_SCAN_COMPLETE: scan completed
+ * @IWL_FW_INI_APPLY_NUM: number of apply points
+*/
+enum iwl_fw_ini_apply_point {
+       IWL_FW_INI_APPLY_INVALID,
+       IWL_FW_INI_APPLY_EARLY,
+       IWL_FW_INI_APPLY_AFTER_ALIVE,
+       IWL_FW_INI_APPLY_POST_INIT,
+       IWL_FW_INI_APPLY_MISSED_BEACONS,
+       IWL_FW_INI_APPLY_SCAN_COMPLETE,
+       IWL_FW_INI_APPLY_NUM,
+}; /* FW_INI_APPLY_POINT_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_allocation_id
+ * @IWL_FW_INI_ALLOCATION_INVALID: invalid
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC1: allocation meant for DBGC1 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC2: allocation meant for DBGC2 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_DBGC3: allocation meant for DBGC3 configuration
+ * @IWL_FW_INI_ALLOCATION_ID_SDFX: for SDFX module
+ * @IWL_FW_INI_ALLOCATION_ID_FW_DUMP: used for crash and runtime dumps
+ * @IWL_FW_INI_ALLOCATION_ID_USER_DEFINED: for future user scenarios
+*/
+enum iwl_fw_ini_allocation_id {
+       IWL_FW_INI_ALLOCATION_INVALID,
+       IWL_FW_INI_ALLOCATION_ID_DBGC1,
+       IWL_FW_INI_ALLOCATION_ID_DBGC2,
+       IWL_FW_INI_ALLOCATION_ID_DBGC3,
+       IWL_FW_INI_ALLOCATION_ID_SDFX,
+       IWL_FW_INI_ALLOCATION_ID_FW_DUMP,
+       IWL_FW_INI_ALLOCATION_ID_USER_DEFINED,
+}; /* FW_INI_ALLOCATION_ID_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_buffer_location
+ * @IWL_FW_INI_LOCATION_INVALID: invalid
+ * @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location
+ * @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location
+ */
+enum iwl_fw_ini_buffer_location {
+       IWL_FW_INI_LOCATION_SRAM_INVALID,
+       IWL_FW_INI_LOCATION_SRAM_PATH,
+       IWL_FW_INI_LOCATION_DRAM_PATH,
+}; /* FW_INI_BUFFER_LOCATION_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_debug_flow
+ * @IWL_FW_INI_DEBUG_INVALID: invalid
+ * @IWL_FW_INI_DEBUG_DBTR_FLOW: undefined
+ * @IWL_FW_INI_DEBUG_TB2DTF_FLOW: undefined
+ */
+enum iwl_fw_ini_debug_flow {
+       IWL_FW_INI_DEBUG_INVALID,
+       IWL_FW_INI_DEBUG_DBTR_FLOW,
+       IWL_FW_INI_DEBUG_TB2DTF_FLOW,
+}; /* FW_INI_DEBUG_FLOW_E_VER_1 */
+
+/**
+ * enum iwl_fw_ini_region_type
+ * @IWL_FW_INI_REGION_INVALID: invalid
+ * @IWL_FW_INI_REGION_DEVICE_MEMORY: device internal memory
+ * @IWL_FW_INI_REGION_PERIPHERY_MAC: periphery registers of MAC
+ * @IWL_FW_INI_REGION_PERIPHERY_PHY: periphery registers of PHY
+ * @IWL_FW_INI_REGION_PERIPHERY_AUX: periphery registers of AUX
+ * @IWL_FW_INI_REGION_DRAM_BUFFER: DRAM buffer
+ * @IWL_FW_INI_REGION_DRAM_IMR: IMR memory
+ * @IWL_FW_INI_REGION_INTERNAL_BUFFER: undefined
+ * @IWL_FW_INI_REGION_TXF: TX fifos
+ * @IWL_FW_INI_REGION_RXF: RX fifo
+ * @IWL_FW_INI_REGION_PAGING: paging memory
+ * @IWL_FW_INI_REGION_CSR: CSR registers
+ * @IWL_FW_INI_REGION_NUM: number of region types
+ */
+enum iwl_fw_ini_region_type {
+       IWL_FW_INI_REGION_INVALID,
+       IWL_FW_INI_REGION_DEVICE_MEMORY,
+       IWL_FW_INI_REGION_PERIPHERY_MAC,
+       IWL_FW_INI_REGION_PERIPHERY_PHY,
+       IWL_FW_INI_REGION_PERIPHERY_AUX,
+       IWL_FW_INI_REGION_DRAM_BUFFER,
+       IWL_FW_INI_REGION_DRAM_IMR,
+       IWL_FW_INI_REGION_INTERNAL_BUFFER,
+       IWL_FW_INI_REGION_TXF,
+       IWL_FW_INI_REGION_RXF,
+       IWL_FW_INI_REGION_PAGING,
+       IWL_FW_INI_REGION_CSR,
+       IWL_FW_INI_REGION_NUM
+}; /* FW_INI_REGION_TYPE_E_VER_1*/
+
+#endif
index 1dd23f8..2c9e40e 100644 (file)
@@ -151,9 +151,9 @@ enum iwl_tsf_id {
  * @beacon_time: beacon transmit time in system time
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
+ * @reserved1: reserved
  * @dtim_interval: dtim transmit time in TU
- * @dtim_reciprocal: 2^32 / dtim_interval
+ * @reserved2: reserved
  * @mcast_qid: queue ID for multicast traffic.
  *     NOTE: obsolete from VER2 and on
  * @beacon_template: beacon template ID
@@ -162,9 +162,9 @@ struct iwl_mac_data_ap {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved1;
        __le32 dtim_interval;
-       __le32 dtim_reciprocal;
+       __le32 reserved2;
        __le32 mcast_qid;
        __le32 beacon_template;
 } __packed; /* AP_MAC_DATA_API_S_VER_2 */
@@ -174,26 +174,34 @@ struct iwl_mac_data_ap {
  * @beacon_time: beacon transmit time in system time
  * @beacon_tsf: beacon transmit time in TSF
  * @bi: beacon interval in TU
- * @bi_reciprocal: 2^32 / bi
+ * @reserved: reserved
  * @beacon_template: beacon template ID
  */
 struct iwl_mac_data_ibss {
        __le32 beacon_time;
        __le64 beacon_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved;
        __le32 beacon_template;
 } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
 
 /**
+ * enum iwl_mac_data_policy - policy of the data path for this MAC
+ * @TWT_SUPPORTED: twt is supported
+ */
+enum iwl_mac_data_policy {
+       TWT_SUPPORTED   = BIT(0),
+};
+
+/**
  * struct iwl_mac_data_sta - configuration data for station MAC context
  * @is_assoc: 1 for associated state, 0 otherwise
  * @dtim_time: DTIM arrival time in system time
  * @dtim_tsf: DTIM arrival time in TSF
  * @bi: beacon interval in TU, applicable only when associated
- * @bi_reciprocal: 2^32 / bi , applicable only when associated
+ * @reserved1: reserved
  * @dtim_interval: DTIM interval in TU, applicable only when associated
- * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
+ * @data_policy: see &enum iwl_mac_data_policy
  * @listen_interval: in beacon intervals, applicable only when associated
  * @assoc_id: unique ID assigned by the AP during association
  * @assoc_beacon_arrive_time: TSF of first beacon after association
@@ -203,13 +211,13 @@ struct iwl_mac_data_sta {
        __le32 dtim_time;
        __le64 dtim_tsf;
        __le32 bi;
-       __le32 bi_reciprocal;
+       __le32 reserved1;
        __le32 dtim_interval;
-       __le32 dtim_reciprocal;
+       __le32 data_policy;
        __le32 listen_interval;
        __le32 assoc_id;
        __le32 assoc_beacon_arrive_time;
-} __packed; /* STA_MAC_DATA_API_S_VER_1 */
+} __packed; /* STA_MAC_DATA_API_S_VER_2 */
 
 /**
  * struct iwl_mac_data_go - configuration data for P2P GO MAC context
@@ -233,7 +241,7 @@ struct iwl_mac_data_go {
 struct iwl_mac_data_p2p_sta {
        struct iwl_mac_data_sta sta;
        __le32 ctwin;
-} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
+} __packed; /* P2P_STA_MAC_DATA_API_S_VER_2 */
 
 /**
  * struct iwl_mac_data_pibss - Pseudo IBSS config data
@@ -378,13 +386,6 @@ struct iwl_mac_ctx_cmd {
        };
 } __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
 
-static inline u32 iwl_mvm_reciprocal(u32 v)
-{
-       if (!v)
-               return 0;
-       return 0xFFFFFFFF / v;
-}
-
 #define IWL_NONQOS_SEQ_GET     0x1
 #define IWL_NONQOS_SEQ_SET     0x2
 struct iwl_nonqos_seq_query_cmd {
index c167570..76050cc 100644 (file)
@@ -225,22 +225,18 @@ static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt,
        *dump_data = iwl_fw_error_next_data(*dump_data);
 }
 
-static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
-                             struct iwl_fw_error_dump_data **dump_data)
+static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **dump_data)
 {
-       struct iwl_fw_error_dump_fifo *fifo_hdr;
        struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
-       u32 *fifo_data;
-       u32 fifo_len;
        unsigned long flags;
-       int i, j;
 
-       IWL_DEBUG_INFO(fwrt, "WRT FIFO dump\n");
+       IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n");
 
        if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
                return;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RXF)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) {
                /* Pull RXF1 */
                iwl_fwrt_dump_rxf(fwrt, dump_data,
                                  cfg->lmac[0].rxfifo1_size, 0, 0);
@@ -254,7 +250,25 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
                                          LMAC2_PRPH_OFFSET, 2);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_TXF)) {
+       iwl_trans_release_nic_access(fwrt->trans, &flags);
+}
+
+static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **dump_data)
+{
+       struct iwl_fw_error_dump_fifo *fifo_hdr;
+       struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
+       u32 *fifo_data;
+       u32 fifo_len;
+       unsigned long flags;
+       int i, j;
+
+       IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n");
+
+       if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))
+               return;
+
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) {
                /* Pull TXF data from LMAC1 */
                for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) {
                        /* Mark the number of TXF we're pulling now */
@@ -279,7 +293,7 @@ static void iwl_fw_dump_fifos(struct iwl_fw_runtime *fwrt,
                }
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
            fw_has_capa(&fwrt->fw->ucode_capa,
                        IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
                /* Pull UMAC internal TXF data from all TXFs */
@@ -595,16 +609,16 @@ static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
        do {size_t item = item_len; len += (!!item) * const_len + item; } \
        while (0)
 
-static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
-                          struct iwl_fwrt_shared_mem_cfg *mem_cfg)
+static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
+                         struct iwl_fwrt_shared_mem_cfg *mem_cfg)
 {
        size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
                         sizeof(struct iwl_fw_error_dump_fifo);
        u32 fifo_len = 0;
        int i;
 
-       if (!(fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RXF)))
-               goto dump_txf;
+       if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF))
+               return 0;
 
        /* Count RXF2 size */
        ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
@@ -613,8 +627,18 @@ static int iwl_fw_fifo_len(struct iwl_fw_runtime *fwrt,
        for (i = 0; i < mem_cfg->num_lmacs; i++)
                ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
 
-dump_txf:
-       if (!(fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_TXF)))
+       return fifo_len;
+}
+
+static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
+                         struct iwl_fwrt_shared_mem_cfg *mem_cfg)
+{
+       size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) +
+                        sizeof(struct iwl_fw_error_dump_fifo);
+       u32 fifo_len = 0;
+       int i;
+
+       if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF))
                goto dump_internal_txf;
 
        /* Count TXF sizes */
@@ -627,7 +651,7 @@ dump_txf:
        }
 
 dump_internal_txf:
-       if (!((fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_INTERNAL_TXF)) &&
+       if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
              fw_has_capa(&fwrt->fw->ucode_capa,
                          IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)))
                goto out;
@@ -639,6 +663,32 @@ out:
        return fifo_len;
 }
 
+static void iwl_dump_paging(struct iwl_fw_runtime *fwrt,
+                           struct iwl_fw_error_dump_data **data)
+{
+       int i;
+
+       IWL_DEBUG_INFO(fwrt, "WRT paging dump\n");
+       for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
+               struct iwl_fw_error_dump_paging *paging;
+               struct page *pages =
+                       fwrt->fw_paging_db[i].fw_paging_block;
+               dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
+
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+               (*data)->len = cpu_to_le32(sizeof(*paging) +
+                                            PAGING_BLOCK_SIZE);
+               paging =  (void *)(*data)->data;
+               paging->index = cpu_to_le32(i);
+               dma_sync_single_for_cpu(fwrt->trans->dev, addr,
+                                       PAGING_BLOCK_SIZE,
+                                       DMA_BIDIRECTIONAL);
+               memcpy(paging->data, page_address(pages),
+                      PAGING_BLOCK_SIZE);
+               (*data) = iwl_fw_error_next_data(*data);
+       }
+}
+
 static struct iwl_fw_error_dump_file *
 _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
                   struct iwl_fw_dump_ptrs *fw_error_dump)
@@ -655,13 +705,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->smem_len;
        u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ?
                                0 : fwrt->trans->cfg->dccm2_len;
-       bool monitor_dump_only = false;
        int i;
 
-       if (fwrt->dump.trig &&
-           fwrt->dump.trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
-               monitor_dump_only = true;
-
        /* SRAM - include stack CCM if driver knows the values for it */
        if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) {
                const struct fw_img *img;
@@ -676,26 +721,27 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
 
        /* reading RXF/TXF sizes */
        if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
-               fifo_len = iwl_fw_fifo_len(fwrt, mem_cfg);
+               fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
+               fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
 
                /* Make room for PRPH registers */
                if (!fwrt->trans->cfg->gen2 &&
-                   fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PRPH))
+                  iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
                        prph_len += iwl_fw_get_prph_len(fwrt);
 
                if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 &&
-                   fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_RADIO_REG))
+                   iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
                        radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
        }
 
        file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_DEV_FW_INFO))
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO))
                file_len += sizeof(*dump_data) + sizeof(*dump_info);
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM_CFG))
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG))
                file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg);
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
                size_t hdr_len = sizeof(*dump_data) +
                                 sizeof(struct iwl_fw_error_dump_mem);
 
@@ -712,10 +758,7 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* Make room for fw's virtual image pages, if it exists */
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING) &&
-           !fwrt->trans->cfg->gen2 &&
-           fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
-           fwrt->fw_paging_db[0].fw_paging_block)
+       if (iwl_fw_dbg_is_paging_enabled(fwrt))
                file_len += fwrt->num_of_paging_blk *
                        (sizeof(*dump_data) +
                         sizeof(struct iwl_fw_error_dump_paging) +
@@ -727,12 +770,12 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* If we only want a monitor dump, reset the file length */
-       if (monitor_dump_only) {
+       if (fwrt->dump.monitor_only) {
                file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 +
                           sizeof(*dump_info) + sizeof(*dump_smem_cfg);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_ERROR_INFO) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
            fwrt->dump.desc)
                file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
                            fwrt->dump.desc->len;
@@ -746,7 +789,7 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
        dump_data = (void *)dump_file->data;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
                dump_data->len = cpu_to_le32(sizeof(*dump_info));
                dump_info = (void *)dump_data->data;
@@ -767,7 +810,7 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
                dump_data = iwl_fw_error_next_data(dump_data);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM_CFG)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) {
                /* Dump shared memory configuration */
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG);
                dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg));
@@ -799,12 +842,13 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
 
        /* We only dump the FIFOs if the FW is in error state */
        if (fifo_len) {
-               iwl_fw_dump_fifos(fwrt, &dump_data);
+               iwl_fw_dump_rxf(fwrt, &dump_data);
+               iwl_fw_dump_txf(fwrt, &dump_data);
                if (radio_len)
                        iwl_read_radio_regs(fwrt, &dump_data);
        }
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_ERROR_INFO) &&
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
            fwrt->dump.desc) {
                dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
                dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
@@ -817,10 +861,10 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* In case we only want monitor dump, skip to dump trasport data */
-       if (monitor_dump_only)
+       if (fwrt->dump.monitor_only)
                goto out;
 
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_MEM)) {
+       if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
                const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem =
                        fwrt->fw->dbg.mem_tlv;
 
@@ -865,30 +909,8 @@ _iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
        }
 
        /* Dump fw's virtual image */
-       if (fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING) &&
-           !fwrt->trans->cfg->gen2 &&
-           fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
-           fwrt->fw_paging_db[0].fw_paging_block) {
-               IWL_DEBUG_INFO(fwrt, "WRT paging dump\n");
-               for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
-                       struct iwl_fw_error_dump_paging *paging;
-                       struct page *pages =
-                               fwrt->fw_paging_db[i].fw_paging_block;
-                       dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
-
-                       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
-                       dump_data->len = cpu_to_le32(sizeof(*paging) +
-                                                    PAGING_BLOCK_SIZE);
-                       paging = (void *)dump_data->data;
-                       paging->index = cpu_to_le32(i);
-                       dma_sync_single_for_cpu(fwrt->trans->dev, addr,
-                                               PAGING_BLOCK_SIZE,
-                                               DMA_BIDIRECTIONAL);
-                       memcpy(paging->data, page_address(pages),
-                              PAGING_BLOCK_SIZE);
-                       dump_data = iwl_fw_error_next_data(dump_data);
-               }
-       }
+       if (iwl_fw_dbg_is_paging_enabled(fwrt))
+               iwl_dump_paging(fwrt, &dump_data);
 
        if (prph_len) {
                iwl_dump_prph(fwrt->trans, &dump_data,
@@ -912,6 +934,7 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
        struct iwl_fw_error_dump_file *dump_file;
        struct scatterlist *sg_dump_data;
        u32 file_len;
+       u32 dump_mask = fwrt->fw->dbg.dump_mask;
 
        IWL_DEBUG_INFO(fwrt, "WRT dump start\n");
 
@@ -931,8 +954,10 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt)
                goto out;
        }
 
-       fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans,
-                                                      fwrt->dump.trig);
+       if (fwrt->dump.monitor_only)
+               dump_mask &= IWL_FW_ERROR_DUMP_FW_MONITOR;
+
+       fw_error_dump->trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask);
        file_len = le32_to_cpu(dump_file->file_len);
        fw_error_dump->fwrt_len = file_len;
        if (fw_error_dump->trans_ptr) {
@@ -973,6 +998,14 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = {
 };
 IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
 
+void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt)
+{
+       IWL_INFO(fwrt, "error dump due to fw assert\n");
+       fwrt->dump.desc = &iwl_dump_desc_assert;
+       iwl_fw_error_dump(fwrt);
+}
+IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump);
+
 void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt)
 {
        struct iwl_fw_dump_desc *iwl_dump_desc_no_alive =
@@ -998,7 +1031,8 @@ void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt)
 IWL_EXPORT_SYMBOL(iwl_fw_alive_error_dump);
 
 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
-                           const struct iwl_fw_dump_desc *desc, void *trigger,
+                           const struct iwl_fw_dump_desc *desc,
+                           bool monitor_only,
                            unsigned int delay)
 {
        /*
@@ -1028,7 +1062,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
                 le32_to_cpu(desc->trig_desc.type));
 
        fwrt->dump.desc = desc;
-       fwrt->dump.trig = trigger;
+       fwrt->dump.monitor_only = monitor_only;
 
        schedule_delayed_work(&fwrt->dump.wk, delay);
 
@@ -1043,6 +1077,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
 {
        struct iwl_fw_dump_desc *desc;
        unsigned int delay = 0;
+       bool monitor_only = false;
 
        if (trigger) {
                u16 occurrences = le16_to_cpu(trigger->occurrences) - 1;
@@ -1059,6 +1094,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
 
                trigger->occurrences = cpu_to_le16(occurrences);
                delay = le16_to_cpu(trigger->trig_dis_ms);
+               monitor_only = trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY;
        }
 
        desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
@@ -1070,7 +1106,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
        desc->trig_desc.type = cpu_to_le32(trig);
        memcpy(desc->trig_desc.data, str, len);
 
-       return iwl_fw_dbg_collect_desc(fwrt, desc, trigger, delay);
+       return iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay);
 }
 IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect);
 
@@ -1081,6 +1117,9 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
        int ret, len = 0;
        char buf[64];
 
+       if (fwrt->trans->ini_valid)
+               return 0;
+
        if (fmt) {
                va_list ap;
 
@@ -1224,3 +1263,210 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)
                                 cfg->d3_debug_data_length);
 }
 IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data);
+
+static void
+iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt,
+                            struct iwl_fw_ini_allocation_tlv *alloc)
+{
+       struct iwl_trans *trans = fwrt->trans;
+       struct iwl_continuous_record_cmd cont_rec = {};
+       struct iwl_buffer_allocation_cmd *cmd = (void *)&cont_rec.pad[0];
+       struct iwl_host_cmd hcmd = {
+               .id = LDBG_CONFIG_CMD,
+               .flags = CMD_ASYNC,
+               .data[0] = &cont_rec,
+               .len[0] = sizeof(cont_rec),
+       };
+       void *virtual_addr = NULL;
+       u32 size = le32_to_cpu(alloc->size);
+       dma_addr_t phys_addr;
+
+       cont_rec.record_mode.enable_recording = cpu_to_le16(BUFFER_ALLOCATION);
+
+       if (!trans->num_blocks &&
+           le32_to_cpu(alloc->buffer_location) !=
+           IWL_FW_INI_LOCATION_DRAM_PATH)
+               return;
+
+       virtual_addr = dma_alloc_coherent(fwrt->trans->dev, size,
+                                         &phys_addr, GFP_KERNEL);
+
+       /* TODO: alloc fragments if needed */
+       if (!virtual_addr)
+               IWL_ERR(fwrt, "Failed to allocate debug memory\n");
+
+       if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon)))
+               return;
+
+       trans->fw_mon[trans->num_blocks].block = virtual_addr;
+       trans->fw_mon[trans->num_blocks].physical = phys_addr;
+       trans->fw_mon[trans->num_blocks].size = size;
+       trans->num_blocks++;
+
+       IWL_DEBUG_FW(trans, "Allocated debug block of size %d\n", size);
+
+       /* First block is assigned via registers / context info */
+       if (trans->num_blocks == 1)
+               return;
+
+       cmd->num_frags = cpu_to_le32(1);
+       cmd->fragments[0].address = cpu_to_le64(phys_addr);
+       cmd->fragments[0].size = alloc->size;
+       cmd->allocation_id = alloc->allocation_id;
+       cmd->buffer_location = alloc->buffer_location;
+
+       iwl_trans_send_cmd(trans, &hcmd);
+}
+
+static void iwl_fw_dbg_send_hcmd(struct iwl_fw_runtime *fwrt,
+                                struct iwl_ucode_tlv *tlv)
+{
+       struct iwl_fw_ini_hcmd_tlv *hcmd_tlv = (void *)&tlv->data[0];
+       struct iwl_fw_ini_hcmd *data = &hcmd_tlv->hcmd;
+       u16 len = le32_to_cpu(tlv->length) - sizeof(*hcmd_tlv);
+
+       struct iwl_host_cmd hcmd = {
+               .id = WIDE_ID(data->group, data->id),
+               .len = { len, },
+               .data = { data->data, },
+       };
+
+       iwl_trans_send_cmd(fwrt->trans, &hcmd);
+}
+
+static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt,
+                                     struct iwl_fw_ini_region_tlv *tlv,
+                                     bool ext, enum iwl_fw_ini_apply_point pnt)
+{
+       void *iter = (void *)tlv->region_config;
+       int i, size = le32_to_cpu(tlv->num_regions);
+
+       for (i = 0; i < size; i++) {
+               struct iwl_fw_ini_region_cfg *reg = iter;
+               int id = le32_to_cpu(reg->region_id);
+               struct iwl_fw_ini_active_regs *active;
+
+               if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_regs)))
+                       break;
+
+               active = &fwrt->dump.active_regs[id];
+
+               if (ext && active->apply_point == pnt)
+                       IWL_WARN(fwrt->trans,
+                                "External region TLV overrides FW default %x\n",
+                                id);
+
+               IWL_DEBUG_FW(fwrt,
+                            "%s: apply point %d, activating region ID %d\n",
+                            __func__, pnt, id);
+
+               active->reg = reg;
+               active->apply_point = pnt;
+
+               if (le32_to_cpu(reg->region_type) !=
+                   IWL_FW_INI_REGION_DRAM_BUFFER)
+                       iter += le32_to_cpu(reg->num_regions) * sizeof(__le32);
+
+               iter += sizeof(*reg);
+       }
+}
+
+static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt,
+                                      struct iwl_fw_ini_trigger_tlv *tlv,
+                                      bool ext,
+                                      enum iwl_fw_ini_apply_point apply_point)
+{
+       int i, size = le32_to_cpu(tlv->num_triggers);
+       void *iter = (void *)tlv->trigger_config;
+
+       for (i = 0; i < size; i++) {
+               struct iwl_fw_ini_trigger *trig = iter;
+               struct iwl_fw_ini_active_triggers *active;
+               int id = le32_to_cpu(trig->trigger_id);
+               u32 num;
+
+               if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)))
+                       break;
+
+               active = &fwrt->dump.active_trigs[id];
+
+               if (active->apply_point != apply_point) {
+                       active->conf = NULL;
+                       active->conf_ext = NULL;
+               }
+
+               num = le32_to_cpu(trig->num_regions);
+
+               if (ext && active->apply_point == apply_point) {
+                       num += le32_to_cpu(active->conf->num_regions);
+                       if (trig->ignore_default) {
+                               active->conf_ext = active->conf;
+                               active->conf = trig;
+                       } else {
+                               active->conf_ext = trig;
+                       }
+               } else {
+                       active->conf = trig;
+               }
+
+               iter += sizeof(*trig) +
+                       le32_to_cpu(trig->num_regions) * sizeof(__le32);
+
+               active->active = num;
+               active->apply_point = apply_point;
+       }
+}
+
+static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                                   struct iwl_apply_point_data *data,
+                                   enum iwl_fw_ini_apply_point pnt,
+                                   bool ext)
+{
+       void *iter = data->data;
+
+       while (iter && iter < data->data + data->size) {
+               struct iwl_ucode_tlv *tlv = iter;
+               void *ini_tlv = (void *)tlv->data;
+               u32 type = le32_to_cpu(tlv->type);
+
+               switch (type) {
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+                       iwl_fw_dbg_buffer_allocation(fwrt, ini_tlv);
+                       break;
+               case IWL_UCODE_TLV_TYPE_HCMD:
+                       if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) {
+                               IWL_ERR(fwrt,
+                                       "Invalid apply point %x for host command\n",
+                                       pnt);
+                               goto next;
+                       }
+                       iwl_fw_dbg_send_hcmd(fwrt, tlv);
+                       break;
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+                       iwl_fw_dbg_update_regions(fwrt, ini_tlv, ext, pnt);
+                       break;
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+                       iwl_fw_dbg_update_triggers(fwrt, ini_tlv, ext, pnt);
+                       break;
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       break;
+               default:
+                       WARN_ONCE(1, "Invalid TLV %x for apply point\n", type);
+                       break;
+               }
+next:
+               iter += sizeof(*tlv) + le32_to_cpu(tlv->length);
+       }
+}
+
+void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                           enum iwl_fw_ini_apply_point apply_point)
+{
+       void *data = &fwrt->trans->apply_points[apply_point];
+
+       _iwl_fw_dbg_apply_point(fwrt, data, apply_point, false);
+
+       data = &fwrt->trans->apply_points_ext[apply_point];
+       _iwl_fw_dbg_apply_point(fwrt, data, apply_point, true);
+}
+IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point);
index 6f8d325..ab81ea8 100644 (file)
@@ -72,6 +72,7 @@
 #include "file.h"
 #include "error-dump.h"
 #include "api/commands.h"
+#include "api/dbg-tlv.h"
 
 /**
  * struct iwl_fw_dump_desc - describes the dump
@@ -101,13 +102,12 @@ static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt)
        if (fwrt->dump.desc != &iwl_dump_desc_assert)
                kfree(fwrt->dump.desc);
        fwrt->dump.desc = NULL;
-       fwrt->dump.trig = NULL;
 }
 
 void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt);
 int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
                            const struct iwl_fw_dump_desc *desc,
-                           void *trigger, unsigned int delay);
+                           bool monitor_only, unsigned int delay);
 int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
                       enum iwl_fw_dbg_trigger trig,
                       const char *str, size_t len,
@@ -193,6 +193,9 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt,
 {
        struct iwl_fw_dbg_trigger_tlv *trig;
 
+       if (fwrt->trans->ini_valid)
+               return NULL;
+
        if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id))
                return NULL;
 
@@ -263,6 +266,9 @@ _iwl_fw_dbg_stop_recording(struct iwl_trans *trans,
        iwl_write_prph(trans, DBGC_IN_SAMPLE, 0);
        udelay(100);
        iwl_write_prph(trans, DBGC_OUT_CTRL, 0);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       trans->dbg_rec_on = false;
+#endif
 }
 
 static inline void
@@ -293,6 +299,14 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans,
        }
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt)
+{
+       if (fwrt->fw->dbg.dest_tlv && fwrt->cur_fw_img == IWL_UCODE_REGULAR)
+               fwrt->trans->dbg_rec_on = true;
+}
+#endif
+
 static inline void
 iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
                             struct iwl_fw_dbg_params *params)
@@ -301,6 +315,9 @@ iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt,
                _iwl_fw_dbg_restart_recording(fwrt->trans, params);
        else
                iwl_fw_dbg_start_stop_hcmd(fwrt, true);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       iwl_fw_set_dbg_rec_on(fwrt);
+#endif
 }
 
 static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
@@ -310,12 +327,25 @@ static inline void iwl_fw_dump_conf_clear(struct iwl_fw_runtime *fwrt)
 
 void iwl_fw_error_dump_wk(struct work_struct *work);
 
+static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type)
+{
+       return (fwrt->fw->dbg.dump_mask & BIT(type));
+}
+
 static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt)
 {
        return fw_has_capa(&fwrt->fw->ucode_capa,
                           IWL_UCODE_TLV_CAPA_D3_DEBUG) &&
                fwrt->trans->cfg->d3_debug_data_length &&
-               fwrt->fw->dbg.dump_mask & BIT(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
+               iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
+}
+
+static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt)
+{
+       return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) &&
+               !fwrt->trans->cfg->gen2 &&
+               fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size &&
+               fwrt->fw_paging_db[0].fw_paging_block;
 }
 
 void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt);
@@ -366,6 +396,10 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
 
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 
+void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt);
 void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt);
 void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt);
+void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
+                           enum iwl_fw_ini_apply_point apply_point);
+
 #endif  /* __iwl_fw_dbg_h__ */
index 6005a41..81f557c 100644 (file)
@@ -91,6 +91,8 @@ struct iwl_ucode_header {
        } u;
 };
 
+#define IWL_UCODE_INI_TLV_GROUP        BIT(24)
+
 /*
  * new TLV uCode file layout
  *
@@ -141,6 +143,11 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_FW_GSCAN_CAPA     = 50,
        IWL_UCODE_TLV_FW_MEM_SEG        = 51,
        IWL_UCODE_TLV_IML               = 52,
+       IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION    = IWL_UCODE_INI_TLV_GROUP | 0x1,
+       IWL_UCODE_TLV_TYPE_HCMD                 = IWL_UCODE_INI_TLV_GROUP | 0x2,
+       IWL_UCODE_TLV_TYPE_REGIONS              = IWL_UCODE_INI_TLV_GROUP | 0x3,
+       IWL_UCODE_TLV_TYPE_TRIGGERS             = IWL_UCODE_INI_TLV_GROUP | 0x4,
+       IWL_UCODE_TLV_TYPE_DEBUG_FLOW           = IWL_UCODE_INI_TLV_GROUP | 0x5,
 
        /* TLVs 0x1000-0x2000 are for internal driver usage */
        IWL_UCODE_TLV_FW_DBG_DUMP_LST   = 0x1000,
index 54dbbd9..1233316 100644 (file)
@@ -65,6 +65,8 @@
 #define __iwl_fw_img_h__
 #include <linux/types.h>
 
+#include "api/dbg-tlv.h"
+
 #include "file.h"
 #include "error-dump.h"
 
@@ -221,6 +223,30 @@ struct iwl_fw_dbg {
 };
 
 /**
+ * struct iwl_fw_ini_active_triggers
+ * @active: is this trigger active
+ * @apply_point: last apply point that updated this trigger
+ * @conf: active trigger
+ * @conf_ext: second trigger, contains extra regions to dump
+ */
+struct iwl_fw_ini_active_triggers {
+       bool active;
+       enum iwl_fw_ini_apply_point apply_point;
+       struct iwl_fw_ini_trigger *conf;
+       struct iwl_fw_ini_trigger *conf_ext;
+};
+
+/**
+ * struct iwl_fw_ini_active_regs
+ * @reg: active region from TLV
+ * @apply_point: apply point where it became active
+ */
+struct iwl_fw_ini_active_regs {
+       struct iwl_fw_ini_region_cfg *reg;
+       enum iwl_fw_ini_apply_point apply_point;
+};
+
+/**
  * struct iwl_fw - variables associated with the firmware
  *
  * @ucode_ver: ucode version from the ucode file
index 2b8b50a..204ad5b 100644 (file)
@@ -64,6 +64,7 @@
 #include "iwl-trans.h"
 #include "img.h"
 #include "fw/api/debug.h"
+#include "fw/api/dbg-tlv.h"
 #include "fw/api/paging.h"
 #include "iwl-eeprom-parse.h"
 
@@ -131,7 +132,7 @@ struct iwl_fw_runtime {
        /* debug */
        struct {
                const struct iwl_fw_dump_desc *desc;
-               const struct iwl_fw_dbg_trigger_tlv *trig;
+               bool monitor_only;
                struct delayed_work wk;
 
                u8 conf;
@@ -139,6 +140,8 @@ struct iwl_fw_runtime {
                /* ts of the beginning of a non-collect fw dbg data period */
                unsigned long non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1];
                u32 *d3_debug_data;
+               struct iwl_fw_ini_active_regs active_regs[IWL_FW_INI_MAX_REGION_ID];
+               struct iwl_fw_ini_active_triggers active_trigs[IWL_FW_TRIGGER_ID_NUM];
        } dump;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct {
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
new file mode 100644 (file)
index 0000000..bc1e4d4
--- /dev/null
@@ -0,0 +1,230 @@
+/******************************************************************************
+ *
+ * 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) 2018 Intel 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/firmware.h>
+#include "iwl-trans.h"
+#include "iwl-dbg-tlv.h"
+
+void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
+                        bool ext)
+{
+       struct iwl_apply_point_data *data;
+       struct iwl_fw_ini_header *header = (void *)&tlv->data[0];
+       u32 apply_point = le32_to_cpu(header->apply_point);
+
+       int copy_size = le32_to_cpu(tlv->length) + sizeof(*tlv);
+
+       if (WARN_ONCE(apply_point >= IWL_FW_INI_APPLY_NUM,
+                     "Invalid apply point id %d\n", apply_point))
+               return;
+
+       if (ext)
+               data = &trans->apply_points_ext[apply_point];
+       else
+               data = &trans->apply_points[apply_point];
+
+       /*
+        * Make sure we still have room to copy this TLV. Offset points to the
+        * location the last copy ended.
+        */
+       if (WARN_ONCE(data->offset + copy_size > data->size,
+                     "Not enough memory for apply point %d\n",
+                     apply_point))
+               return;
+
+       memcpy(data->data + data->offset, (void *)tlv, copy_size);
+       data->offset += copy_size;
+}
+
+void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
+                      bool ext)
+{
+       struct iwl_ucode_tlv *tlv;
+       u32 size[IWL_FW_INI_APPLY_NUM] = {0};
+       int i;
+
+       while (len >= sizeof(*tlv)) {
+               u32 tlv_len, tlv_type, apply;
+               struct iwl_fw_ini_header *hdr;
+
+               len -= sizeof(*tlv);
+               tlv = (void *)data;
+
+               tlv_len = le32_to_cpu(tlv->length);
+               tlv_type = le32_to_cpu(tlv->type);
+
+               if (len < tlv_len)
+                       return;
+
+               len -= ALIGN(tlv_len, 4);
+               data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+               if (!(tlv_type & IWL_UCODE_INI_TLV_GROUP))
+                       continue;
+
+               hdr = (void *)&tlv->data[0];
+               apply = le32_to_cpu(hdr->apply_point);
+
+               IWL_DEBUG_FW(trans, "Read TLV %x, apply point %d\n",
+                            le32_to_cpu(tlv->type), apply);
+
+               if (WARN_ON(apply >= IWL_FW_INI_APPLY_NUM))
+                       continue;
+
+               size[apply] += sizeof(*tlv) + tlv_len;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(size); i++) {
+               void *mem;
+
+               if (!size[i])
+                       continue;
+
+               mem = kzalloc(size[i], GFP_KERNEL);
+
+               if (!mem) {
+                       IWL_ERR(trans, "No memory for apply point %d\n", i);
+                       return;
+               }
+
+               if (ext) {
+                       trans->apply_points_ext[i].data = mem;
+                       trans->apply_points_ext[i].size = size[i];
+               } else {
+                       trans->apply_points[i].data = mem;
+                       trans->apply_points[i].size = size[i];
+               }
+
+               trans->ini_valid = true;
+       }
+}
+
+void iwl_fw_dbg_free(struct iwl_trans *trans)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(trans->apply_points); i++) {
+               kfree(trans->apply_points[i].data);
+               trans->apply_points[i].size = 0;
+               trans->apply_points[i].offset = 0;
+
+               kfree(trans->apply_points_ext[i].data);
+               trans->apply_points_ext[i].size = 0;
+               trans->apply_points_ext[i].offset = 0;
+       }
+}
+
+static int iwl_parse_fw_dbg_tlv(struct iwl_trans *trans, const u8 *data,
+                               size_t len)
+{
+       struct iwl_ucode_tlv *tlv;
+       enum iwl_ucode_tlv_type tlv_type;
+       u32 tlv_len;
+
+       while (len >= sizeof(*tlv)) {
+               len -= sizeof(*tlv);
+               tlv = (void *)data;
+
+               tlv_len = le32_to_cpu(tlv->length);
+               tlv_type = le32_to_cpu(tlv->type);
+
+               if (len < tlv_len) {
+                       IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
+                               len, tlv_len);
+                       return -EINVAL;
+               }
+               len -= ALIGN(tlv_len, 4);
+               data += sizeof(*tlv) + ALIGN(tlv_len, 4);
+
+               switch (tlv_type) {
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+               case IWL_UCODE_TLV_TYPE_HCMD:
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       iwl_fw_dbg_copy_tlv(trans, tlv, true);
+               default:
+                       WARN_ONCE(1, "Invalid TLV %x\n", tlv_type);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans)
+{
+       const struct firmware *fw;
+       int res;
+
+       if (trans->external_ini_loaded || !iwlwifi_mod_params.enable_ini)
+               return;
+
+       res = request_firmware(&fw, "iwl-dbg-tlv.ini", dev);
+       if (res)
+               return;
+
+       iwl_alloc_dbg_tlv(trans, fw->size, fw->data, true);
+       iwl_parse_fw_dbg_tlv(trans, fw->data, fw->size);
+
+       trans->external_ini_loaded = true;
+       release_firmware(fw);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h
new file mode 100644 (file)
index 0000000..222cd78
--- /dev/null
@@ -0,0 +1,87 @@
+/******************************************************************************
+ *
+ * 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) 2018 Intel 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * 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_dbg_tlv_h__
+#define __iwl_dbg_tlv_h__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+/**
+ * struct iwl_apply_point_data
+ * @data: start address of this apply point data
+ * @size total size of the data
+ * @offset: current offset of the copied data
+ */
+struct iwl_apply_point_data {
+       void *data;
+       int size;
+       int offset;
+};
+
+struct iwl_trans;
+void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans);
+void iwl_fw_dbg_free(struct iwl_trans *trans);
+void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv,
+                        bool ext);
+void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data,
+                      bool ext);
+
+#endif /* __iwl_dbg_tlv_h__*/
index ba41d23..a65ba95 100644 (file)
@@ -72,6 +72,7 @@
 #include "iwl-op-mode.h"
 #include "iwl-agn-hw.h"
 #include "fw/img.h"
+#include "iwl-dbg-tlv.h"
 #include "iwl-config.h"
 #include "iwl-modparams.h"
 
@@ -645,6 +646,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
 
        len -= sizeof(*ucode);
 
+       if (iwlwifi_mod_params.enable_ini)
+               iwl_alloc_dbg_tlv(drv->trans, len, data, false);
+
        while (len >= sizeof(*tlv)) {
                len -= sizeof(*tlv);
                tlv = (void *)data;
@@ -1086,6 +1090,13 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                return -ENOMEM;
                        break;
                        }
+               case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
+               case IWL_UCODE_TLV_TYPE_HCMD:
+               case IWL_UCODE_TLV_TYPE_REGIONS:
+               case IWL_UCODE_TLV_TYPE_TRIGGERS:
+               case IWL_UCODE_TLV_TYPE_DEBUG_FLOW:
+                       if (iwlwifi_mod_params.enable_ini)
+                               iwl_fw_dbg_copy_tlv(drv->trans, tlv, false);
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@ -1565,7 +1576,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans)
        if (!drv->dbgfs_drv) {
                IWL_ERR(drv, "failed to create debugfs directory\n");
                ret = -ENOMEM;
-               goto err_free_drv;
+               goto err_free_tlv;
        }
 
        /* Create transport layer debugfs dir */
@@ -1590,7 +1601,8 @@ err_fw:
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 err_free_dbgfs:
        debugfs_remove_recursive(drv->dbgfs_drv);
-err_free_drv:
+err_free_tlv:
+       iwl_fw_dbg_free(drv->trans);
 #endif
        kfree(drv);
 err:
@@ -1616,9 +1628,13 @@ void iwl_drv_stop(struct iwl_drv *drv)
        mutex_unlock(&iwlwifi_opmode_table_mtx);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
+       drv->trans->ops->debugfs_cleanup(drv->trans);
+
        debugfs_remove_recursive(drv->dbgfs_drv);
 #endif
 
+       iwl_fw_dbg_free(drv->trans);
+
        kfree(drv);
 }
 
@@ -1749,6 +1765,10 @@ MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
 module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644);
 MODULE_PARM_DESC(uapsd_disable,
                 "disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)");
+module_param_named(enable_ini, iwlwifi_mod_params.enable_ini,
+                  bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_ini,
+                "Enable debug INI TLV FW debug infrastructure (default: 0");
 
 /*
  * set bt_coex_active to true, uCode will do kill/defer
index 6fc8dac..73b1c46 100644 (file)
@@ -122,6 +122,7 @@ enum iwl_uapsd_disable {
  * @fw_monitor: allow to use firmware monitor
  * @disable_11ac: disable VHT capabilities, default = false.
  * @remove_when_gone: remove an inaccessible device from the PCIe bus.
+ * @enable_ini: enable new FW debug infratructure (INI TLVs)
  */
 struct iwl_mod_params {
        int swcrypto;
@@ -148,6 +149,7 @@ struct iwl_mod_params {
         */
        bool disable_11ax;
        bool remove_when_gone;
+       bool enable_ini;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 0f51c7b..2084c63 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright(c) 2016        Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel 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
@@ -30,6 +31,7 @@
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright(c) 2016        Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -394,6 +396,7 @@ enum aux_misc_master1_en {
 #define AUX_MISC_MASTER1_SMPHR_STATUS  0xA20800
 #define RSA_ENABLE                     0xA24B08
 #define PREG_AUX_BUS_WPROT_0           0xA04CC0
+#define PREG_PRPH_WPROT_0              0xA04CE0
 #define SB_CPU_1_STATUS                        0xA01E30
 #define SB_CPU_2_STATUS                        0xA01E34
 #define UMAG_SB_CPU_1_STATUS           0xA038C0
@@ -420,4 +423,8 @@ enum {
 #define UREG_CHICK             (0xA05C00)
 #define UREG_CHICK_MSI_ENABLE  BIT(24)
 #define UREG_CHICK_MSIX_ENABLE BIT(25)
+
+#define HPM_DEBUG                      0xA03440
+#define PERSISTENCE_BIT                        BIT(12)
+#define PREG_WFPM_ACCESS               BIT(12)
 #endif                         /* __iwl_prph_h__ */
index 26b3c73..a7009cd 100644 (file)
@@ -73,6 +73,8 @@
 #include "iwl-op-mode.h"
 #include "fw/api/cmdhdr.h"
 #include "fw/api/txq.h"
+#include "fw/api/dbg-tlv.h"
+#include "iwl-dbg-tlv.h"
 
 /**
  * DOC: Transport layer - what is it ?
@@ -534,6 +536,8 @@ struct iwl_trans_rxq_dma_data {
  * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
  *     TX'ed commands and similar. The buffer will be vfree'd by the caller.
  *     Note that the transport must fill in the proper file headers.
+ * @debugfs_cleanup: used in the driver unload flow to make a proper cleanup
+ *     of the trans debugfs
  */
 struct iwl_trans_ops {
 
@@ -602,8 +606,8 @@ struct iwl_trans_ops {
        void (*resume)(struct iwl_trans *trans);
 
        struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
-                                                const struct iwl_fw_dbg_trigger_tlv
-                                                *trigger);
+                                                u32 dump_mask);
+       void (*debugfs_cleanup)(struct iwl_trans *trans);
 };
 
 /**
@@ -679,7 +683,6 @@ enum iwl_plat_pm_mode {
  * enter/exit (in msecs).
  */
 #define IWL_TRANS_IDLE_TIMEOUT 2000
-#define IWL_MAX_DEBUG_ALLOCATIONS      1
 
 /**
  * struct iwl_dram_data
@@ -734,6 +737,7 @@ struct iwl_dram_data {
  * @runtime_pm_mode: the runtime power management mode in use.  This
  *     mode is set during the initialization phase and is not
  *     supposed to change during runtime.
+ * @dbg_rec_on: true iff there is a fw debug recording currently active
  */
 struct iwl_trans {
        const struct iwl_trans_ops *ops;
@@ -774,17 +778,23 @@ struct iwl_trans {
        struct lockdep_map sync_cmd_lockdep_map;
 #endif
 
+       struct iwl_apply_point_data apply_points[IWL_FW_INI_APPLY_NUM];
+       struct iwl_apply_point_data apply_points_ext[IWL_FW_INI_APPLY_NUM];
+
+       bool external_ini_loaded;
+       bool ini_valid;
+
        const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv;
        const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
        struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
-       u32 dbg_dump_mask;
        u8 dbg_n_dest_reg;
        int num_blocks;
-       struct iwl_dram_data fw_mon[IWL_MAX_DEBUG_ALLOCATIONS];
+       struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM];
 
        enum iwl_plat_pm_mode system_pm_mode;
        enum iwl_plat_pm_mode runtime_pm_mode;
        bool suspending;
+       bool dbg_rec_on;
 
        /* pointer to trans specific struct */
        /*Ensure that this pointer will always be aligned to sizeof pointer */
@@ -897,12 +907,11 @@ static inline void iwl_trans_resume(struct iwl_trans *trans)
 }
 
 static inline struct iwl_trans_dump_data *
-iwl_trans_dump_data(struct iwl_trans *trans,
-                   const struct iwl_fw_dbg_trigger_tlv *trigger)
+iwl_trans_dump_data(struct iwl_trans *trans, u32 dump_mask)
 {
        if (!trans->ops->dump_data)
                return NULL;
-       return trans->ops->dump_data(trans, trigger);
+       return trans->ops->dump_data(trans, dump_mask);
 }
 
 static inline struct iwl_device_cmd *
index 843f3b4..9b018d0 100644 (file)
@@ -1956,7 +1956,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
                set_bit(STATUS_FW_ERROR, &mvm->trans->status);
                iwl_mvm_dump_nic_error_log(mvm);
                iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
-                                       NULL, 0);
+                                       false, 0);
                ret = 1;
                goto err;
        }
index 1aa6c7e..33b0af2 100644 (file)
@@ -1299,10 +1299,11 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
        int len;
 
        len = scnprintf(buf, sizeof(buf) - 1,
-                       "traffic=%d\ndbgfs=%d\nvcmd=%d\n",
+                       "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n",
                        !!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
                        !!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
-                       !!(mvmvif->low_latency & LOW_LATENCY_VCMD));
+                       !!(mvmvif->low_latency & LOW_LATENCY_VCMD),
+                       !!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE));
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
@@ -1440,15 +1441,6 @@ static ssize_t iwl_dbgfs_quota_min_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
-static const char * const chanwidths[] = {
-       [NL80211_CHAN_WIDTH_20_NOHT] = "noht",
-       [NL80211_CHAN_WIDTH_20] = "ht20",
-       [NL80211_CHAN_WIDTH_40] = "ht40",
-       [NL80211_CHAN_WIDTH_80] = "vht80",
-       [NL80211_CHAN_WIDTH_80P80] = "vht80p80",
-       [NL80211_CHAN_WIDTH_160] = "vht160",
-};
-
 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
index 2ba8904..c5168ab 100644 (file)
@@ -377,6 +377,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                atomic_set(&mvm->mac80211_queue_stop_count[i], 0);
 
        set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       iwl_fw_set_dbg_rec_on(&mvm->fwrt);
+#endif
        clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status);
 
        return 0;
@@ -407,6 +410,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               iwl_fw_assert_error_dump(&mvm->fwrt);
                goto error;
        }
 
@@ -1036,6 +1040,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        ret = iwl_mvm_load_rt_fw(mvm);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               iwl_fw_assert_error_dump(&mvm->fwrt);
                goto error;
        }
 
index 6486cfb..e049d34 100644 (file)
@@ -767,13 +767,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
        }
 
        ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       ctxt_sta->bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
        ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
                                              vif->bss_conf.dtim_period);
-       ctxt_sta->dtim_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
-                                              vif->bss_conf.dtim_period));
 
        ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
        ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
@@ -782,8 +777,30 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
                cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
 
        if (vif->bss_conf.assoc && vif->bss_conf.he_support &&
-           !iwlwifi_mod_params.disable_11ax)
+           !iwlwifi_mod_params.disable_11ax) {
+               struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+               u8 sta_id = mvmvif->ap_sta_id;
+
                cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
+               if (sta_id != IWL_MVM_INVALID_STA) {
+                       struct ieee80211_sta *sta;
+
+                       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                               lockdep_is_held(&mvm->mutex));
+
+                       /*
+                        * TODO: we should check the ext cap IE but it is
+                        * unclear why the spec requires two bits (one in HE
+                        * cap IE, and one in the ext cap IE). In the meantime
+                        * rely on the HE cap IE only.
+                        */
+                       if (sta && (sta->he_cap.he_cap_elem.mac_cap_info[0] &
+                                   IEEE80211_HE_MAC_CAP0_TWT_RES))
+                               ctxt_sta->data_policy |=
+                                       cpu_to_le32(TWT_SUPPORTED);
+               }
+       }
+
 
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
@@ -832,8 +849,6 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
 
        /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */
        cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       cmd.ibss.bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
 
        /* TODO: Assumes that the beacon id == mac context id */
        cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id);
@@ -965,11 +980,8 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
        tx->tx_flags = cpu_to_le32(tx_flags);
 
        if (!fw_has_capa(&mvm->fw->ucode_capa,
-                        IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
-               mvm->mgmt_last_antenna_idx =
-                       iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                            mvm->mgmt_last_antenna_idx);
-       }
+                        IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION))
+               iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
 
        tx->rate_n_flags =
                cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
@@ -1182,14 +1194,12 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
        }
 
+       if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax)
+               cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX);
+
        ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
-       ctxt_ap->bi_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
        ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
                                             vif->bss_conf.dtim_period);
-       ctxt_ap->dtim_reciprocal =
-               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
-                                              vif->bss_conf.dtim_period));
 
        if (!fw_has_api(&mvm->fw->ucode_capa,
                        IWL_UCODE_TLV_API_STA_TYPE))
index 00f831d..675c52a 100644 (file)
@@ -423,6 +423,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
        ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
        ieee80211_hw_set(hw, DEAUTH_NEED_MGD_TX_PREP);
+       ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
 
        if (iwl_mvm_has_tlc_offload(mvm)) {
                ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
@@ -813,6 +814,21 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
            !ieee80211_is_bufferable_mmpdu(hdr->frame_control))
                sta = NULL;
 
+       /* If there is no sta, and it's not offchannel - send through AP */
+       if (info->control.vif->type == NL80211_IFTYPE_STATION &&
+           info->hw_queue != IWL_MVM_OFFCHANNEL_QUEUE && !sta) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(info->control.vif);
+               u8 ap_sta_id = READ_ONCE(mvmvif->ap_sta_id);
+
+               if (ap_sta_id < IWL_MVM_STATION_COUNT) {
+                       /* mac80211 holds rcu read lock */
+                       sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]);
+                       if (IS_ERR_OR_NULL(sta))
+                               goto drop;
+               }
+       }
+
        if (sta) {
                if (iwl_mvm_defer_tx(mvm, sta, skb))
                        return;
@@ -2383,6 +2399,12 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        /* must be set before quota calculations */
        mvmvif->ap_ibss_active = true;
 
+       if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
+               iwl_mvm_vif_set_low_latency(mvmvif, true,
+                                           LOW_LATENCY_VIF_TYPE);
+               iwl_mvm_send_low_latency_cmd(mvm, true, mvmvif->id);
+       }
+
        /* power updated needs to be done before quotas */
        iwl_mvm_power_update_mac(mvm);
 
@@ -2445,6 +2467,12 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
        mvmvif->ap_ibss_active = false;
        mvm->ap_last_beacon_gp2 = 0;
 
+       if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) {
+               iwl_mvm_vif_set_low_latency(mvmvif, false,
+                                           LOW_LATENCY_VIF_TYPE);
+               iwl_mvm_send_low_latency_cmd(mvm, false,  mvmvif->id);
+       }
+
        iwl_mvm_bt_coex_vif_change(mvm);
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
@@ -2945,6 +2973,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                if (vif->type == NL80211_IFTYPE_AP) {
                        mvmvif->ap_assoc_sta_count++;
                        iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+                       if (vif->bss_conf.he_support &&
+                           !iwlwifi_mod_params.disable_11ax)
+                               iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
                }
 
                iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band,
index 7ba5bc2..707bc7d 100644 (file)
@@ -303,11 +303,13 @@ enum iwl_bt_force_ant_mode {
 * @LOW_LATENCY_TRAFFIC: indicates low latency traffic was detected
 * @LOW_LATENCY_DEBUGFS: low latency mode set from debugfs
 * @LOW_LATENCY_VCMD: low latency mode set from vendor command
+* @LOW_LATENCY_VIF_TYPE: low latency mode set because of vif type (ap)
 */
 enum iwl_mvm_low_latency_cause {
        LOW_LATENCY_TRAFFIC = BIT(0),
        LOW_LATENCY_DEBUGFS = BIT(1),
        LOW_LATENCY_VCMD = BIT(2),
+       LOW_LATENCY_VIF_TYPE = BIT(3),
 };
 
 /**
@@ -844,7 +846,6 @@ struct iwl_mvm {
        u16 hw_queue_to_mac80211[IWL_MAX_TVQM_QUEUES];
 
        struct iwl_mvm_dqa_txq_info queue_info[IWL_MAX_HW_QUEUES];
-       spinlock_t queue_info_lock; /* For syncing queue mgmt operations */
        struct work_struct add_stream_wk; /* To add streams to queues */
 
        atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
@@ -1521,6 +1522,11 @@ static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm)
               mvm->fw->valid_rx_ant;
 }
 
+static inline void iwl_mvm_toggle_tx_ant(struct iwl_mvm *mvm, u8 *ant)
+{
+       *ant = iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), *ant);
+}
+
 static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm)
 {
        u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
@@ -1846,6 +1852,8 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 /* get SystemLowLatencyMode - only needed for beacon threshold? */
 bool iwl_mvm_low_latency(struct iwl_mvm *mvm);
 bool iwl_mvm_low_latency_band(struct iwl_mvm *mvm, enum nl80211_band band);
+void iwl_mvm_send_low_latency_cmd(struct iwl_mvm *mvm, bool low_latency,
+                                 u16 mac_id);
 
 /* get VMACLowLatencyMode */
 static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
index af3fba1..a31dc99 100644 (file)
@@ -676,7 +676,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_LIST_HEAD(&mvm->aux_roc_te_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
        spin_lock_init(&mvm->time_event_lock);
-       spin_lock_init(&mvm->queue_info_lock);
 
        INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
        INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
@@ -770,7 +769,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        memcpy(trans->dbg_conf_tlv, mvm->fw->dbg.conf_tlv,
               sizeof(trans->dbg_conf_tlv));
        trans->dbg_trigger_tlv = mvm->fw->dbg.trigger_tlv;
-       trans->dbg_dump_mask = mvm->fw->dbg.dump_mask;
 
        trans->iml = mvm->fw->iml;
        trans->iml_len = mvm->fw->iml_len;
@@ -846,6 +844,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        iwl_mvm_tof_init(mvm);
 
+       iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
+
        return op_mode;
 
  out_unregister:
@@ -1110,11 +1110,7 @@ static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
 static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       unsigned long mq;
-
-       spin_lock_bh(&mvm->queue_info_lock);
-       mq = mvm->hw_queue_to_mac80211[hw_queue];
-       spin_unlock_bh(&mvm->queue_info_lock);
+       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
 
        iwl_mvm_stop_mac_queues(mvm, mq);
 }
@@ -1140,11 +1136,7 @@ void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
 static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
-       unsigned long mq;
-
-       spin_lock_bh(&mvm->queue_info_lock);
-       mq = mvm->hw_queue_to_mac80211[hw_queue];
-       spin_unlock_bh(&mvm->queue_info_lock);
+       unsigned long mq = mvm->hw_queue_to_mac80211[hw_queue];
 
        iwl_mvm_start_mac_queues(mvm, mq);
 }
@@ -1242,7 +1234,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
         */
        if (!mvm->fw_restart && fw_error) {
                iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
-                                       NULL, 0);
+                                       false, 0);
        } else if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
                struct iwl_mvm_reprobe *reprobe;
 
index 7a98e1a..dabbc04 100644 (file)
@@ -98,8 +98,12 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
 {
        struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       struct ieee80211_sta_he_cap *he_cap = &sta->he_cap;
        u8 supp = 0;
 
+       if (he_cap && he_cap->has_he)
+               return 0;
+
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
                supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
index 26ac940..d5ad390 100644 (file)
@@ -1422,12 +1422,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
        /* update aggregation data for monitor sake on default queue */
        if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
                bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
-               u64 he_phy_data;
-
-               if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560)
-                       he_phy_data = le64_to_cpu(desc->v3.he_phy_data);
-               else
-                       he_phy_data = le64_to_cpu(desc->v1.he_phy_data);
 
                rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
                rx_status->ampdu_reference = mvm->ampdu_ref;
index cfb784f..cb376e8 100644 (file)
@@ -205,9 +205,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum nl80211_band band,
 {
        u32 tx_ant;
 
-       mvm->scan_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                    mvm->scan_last_antenna_idx);
+       iwl_mvm_toggle_tx_ant(mvm, &mvm->scan_last_antenna_idx);
        tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
 
        if (band == NL80211_BAND_2GHZ && !no_cck)
index 1887d2b..e821948 100644 (file)
@@ -319,9 +319,7 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        rcu_read_lock();
 
@@ -372,25 +370,17 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
                return -EINVAL;
 
        if (iwl_mvm_has_new_tx_api(mvm)) {
-               spin_lock_bh(&mvm->queue_info_lock);
-
                if (remove_mac_queue)
                        mvm->hw_queue_to_mac80211[queue] &=
                                ~BIT(mac80211_queue);
 
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                iwl_trans_txq_free(mvm->trans, queue);
 
                return 0;
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
-       if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+       if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0))
                return 0;
-       }
 
        mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
 
@@ -426,10 +416,8 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
                            mvm->hw_queue_to_mac80211[queue]);
 
        /* If the queue is still enabled - nothing left to do in this func */
-       if (cmd.action == SCD_CFG_ENABLE_QUEUE) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+       if (cmd.action == SCD_CFG_ENABLE_QUEUE)
                return 0;
-       }
 
        cmd.sta_id = mvm->queue_info[queue].ra_sta_id;
        cmd.tid = mvm->queue_info[queue].txq_tid;
@@ -448,8 +436,6 @@ static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue,
        /* Regardless if this is a reserved TXQ for a STA - mark it as false */
        mvm->queue_info[queue].reserved = false;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        iwl_trans_txq_disable(mvm->trans, queue, false);
        ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags,
                                   sizeof(struct iwl_scd_txq_cfg_cmd), &cmd);
@@ -474,10 +460,8 @@ static int iwl_mvm_get_queue_agg_tids(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                        lockdep_is_held(&mvm->mutex));
@@ -516,10 +500,8 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        rcu_read_lock();
 
@@ -545,6 +527,16 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
 
        rcu_read_unlock();
 
+       /*
+        * The TX path may have been using this TXQ_ID from the tid_data,
+        * so make sure it's no longer running so that we can safely reuse
+        * this TXQ later. We've set all the TIDs to IWL_MVM_INVALID_QUEUE
+        * above, but nothing guarantees we've stopped using them. Thus,
+        * without this, we could get to iwl_mvm_disable_txq() and remove
+        * the queue while still sending frames to it.
+        */
+       synchronize_net();
+
        return disable_agg_tids;
 }
 
@@ -562,11 +554,9 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        txq_curr_ac = mvm->queue_info[queue].mac80211_ac;
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid = mvm->queue_info[queue].txq_tid;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        same_sta = sta_id == new_sta_id;
 
@@ -610,7 +600,6 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm,
         * by the inactivity checker.
         */
        lockdep_assert_held(&mvm->mutex);
-       lockdep_assert_held(&mvm->queue_info_lock);
 
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
@@ -696,10 +685,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
         * value 3 and VO with value 0, so to check if ac X is lower than ac Y
         * we need to check if the numerical value of X is LARGER than of Y.
         */
-       spin_lock_bh(&mvm->queue_info_lock);
        if (ac <= mvm->queue_info[queue].mac80211_ac && !force) {
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "No redirection needed on TXQ #%d\n",
                                    queue);
@@ -711,7 +697,6 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
        cmd.tid = mvm->queue_info[queue].txq_tid;
        mq = mvm->hw_queue_to_mac80211[queue];
        shared_queue = hweight16(mvm->queue_info[queue].tid_bitmap) > 1;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        IWL_DEBUG_TX_QUEUES(mvm, "Redirecting TXQ #%d to FIFO #%d\n",
                            queue, iwl_mvm_ac_to_tx_fifo[ac]);
@@ -737,9 +722,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
        iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout);
 
        /* Update the TID "owner" of the queue */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].txq_tid = tid;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* TODO: Work-around SCD bug when moving back by multiples of 0x40 */
 
@@ -748,9 +731,7 @@ static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid,
                             cmd.sta_id, tid, IWL_FRAME_LIMIT, ssn);
 
        /* Update AC marking of the queue */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].mac80211_ac = ac;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /*
         * Mark queue as shared in transport if shared
@@ -773,7 +754,7 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,
 {
        int i;
 
-       lockdep_assert_held(&mvm->queue_info_lock);
+       lockdep_assert_held(&mvm->mutex);
 
        /* This should not be hit with new TX path */
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
@@ -853,11 +834,8 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
 {
        bool enable_queue = true;
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /* Make sure this TID isn't already enabled */
        if (mvm->queue_info[queue].tid_bitmap & BIT(tid)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
                IWL_ERR(mvm, "Trying to enable TXQ %d with existing TID %d\n",
                        queue, tid);
                return false;
@@ -893,8 +871,6 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue,
                            queue, mvm->queue_info[queue].tid_bitmap,
                            mvm->hw_queue_to_mac80211[queue]);
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        return enable_queue;
 }
 
@@ -949,9 +925,7 @@ static void iwl_mvm_change_queue_tid(struct iwl_mvm *mvm, int queue)
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        if (WARN(!tid_bitmap, "TXQ %d has no tids assigned to it\n", queue))
                return;
@@ -968,9 +942,7 @@ static void iwl_mvm_change_queue_tid(struct iwl_mvm *mvm, int queue)
                return;
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].txq_tid = tid;
-       spin_unlock_bh(&mvm->queue_info_lock);
        IWL_DEBUG_TX_QUEUES(mvm, "Changed TXQ %d ownership to tid %d\n",
                            queue, tid);
 }
@@ -992,10 +964,8 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)
 
        lockdep_assert_held(&mvm->mutex);
 
-       spin_lock_bh(&mvm->queue_info_lock);
        sta_id = mvm->queue_info[queue].ra_sta_id;
        tid_bitmap = mvm->queue_info[queue].tid_bitmap;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Find TID for queue, and make sure it is the only one on the queue */
        tid = find_first_bit(&tid_bitmap, IWL_MAX_TID_COUNT + 1);
@@ -1052,9 +1022,7 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue)
                }
        }
 
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
-       spin_unlock_bh(&mvm->queue_info_lock);
 }
 
 /*
@@ -1073,7 +1041,7 @@ static bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm,
        int tid;
 
        lockdep_assert_held(&mvmsta->lock);
-       lockdep_assert_held(&mvm->queue_info_lock);
+       lockdep_assert_held(&mvm->mutex);
 
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return false;
@@ -1174,8 +1142,6 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
        if (iwl_mvm_has_new_tx_api(mvm))
                return -ENOSPC;
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        rcu_read_lock();
 
        /* we skip the CMD queue below by starting at 1 */
@@ -1230,12 +1196,7 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
 
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-               /* this isn't so nice, but works OK due to the way we loop */
-               spin_unlock(&mvm->queue_info_lock);
-
-               /* and we need this locking order */
-               spin_lock(&mvmsta->lock);
-               spin_lock(&mvm->queue_info_lock);
+               spin_lock_bh(&mvmsta->lock);
                ret = iwl_mvm_remove_inactive_tids(mvm, mvmsta, i,
                                                   inactive_tid_bitmap,
                                                   &unshare_queues,
@@ -1243,11 +1204,10 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
                if (ret >= 0 && free_queue < 0)
                        free_queue = ret;
                /* only unlock sta lock - we still need the queue info lock */
-               spin_unlock(&mvmsta->lock);
+               spin_unlock_bh(&mvmsta->lock);
        }
 
        rcu_read_unlock();
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Reconfigure queues requiring reconfiguation */
        for_each_set_bit(i, &unshare_queues, IWL_MAX_HW_QUEUES)
@@ -1296,8 +1256,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        tfd_queue_mask = mvmsta->tfd_queue_msk;
        spin_unlock_bh(&mvmsta->lock);
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /*
         * Non-QoS, QoS NDP and MGMT frames should go to a MGMT queue, if one
         * exists
@@ -1327,12 +1285,8 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
                                                IWL_MVM_DQA_MIN_DATA_QUEUE,
                                                IWL_MVM_DQA_MAX_DATA_QUEUE);
        if (queue < 0) {
-               spin_unlock_bh(&mvm->queue_info_lock);
-
                /* try harder - perhaps kill an inactive queue */
                queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id);
-
-               spin_lock_bh(&mvm->queue_info_lock);
        }
 
        /* No free queue - we'll have to share */
@@ -1353,8 +1307,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
        if (queue > 0 && !shared_queue)
                mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        /* This shouldn't happen - out of queues */
        if (WARN_ON(queue <= 0)) {
                IWL_ERR(mvm, "No available queues for tid %d on sta_id %d\n",
@@ -1556,8 +1508,6 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,
        /* run the general cleanup/unsharing of queues */
        iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA);
 
-       spin_lock_bh(&mvm->queue_info_lock);
-
        /* Make sure we have free resources for this STA */
        if (vif_type == NL80211_IFTYPE_STATION && !sta->tdls &&
            !mvm->queue_info[IWL_MVM_DQA_BSS_CLIENT_QUEUE].tid_bitmap &&
@@ -1569,19 +1519,15 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm,
                                                IWL_MVM_DQA_MIN_DATA_QUEUE,
                                                IWL_MVM_DQA_MAX_DATA_QUEUE);
        if (queue < 0) {
-               spin_unlock_bh(&mvm->queue_info_lock);
                /* try again - this time kick out a queue if needed */
                queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id);
                if (queue < 0) {
                        IWL_ERR(mvm, "No available queues for new station\n");
                        return -ENOSPC;
                }
-               spin_lock_bh(&mvm->queue_info_lock);
        }
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_RESERVED;
 
-       spin_unlock_bh(&mvm->queue_info_lock);
-
        mvmsta->reserved_queue = queue;
 
        IWL_DEBUG_TX_QUEUES(mvm, "Reserving data queue #%d for sta_id %d\n",
@@ -1822,6 +1768,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
        if (iwl_mvm_has_tlc_offload(mvm))
                iwl_mvm_rs_add_sta(mvm, mvm_sta);
 
+       iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant);
+
 update_fw:
        ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags);
        if (ret)
@@ -2004,18 +1952,14 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                 * is still marked as IWL_MVM_QUEUE_RESERVED, and
                 * should be manually marked as free again
                 */
-               spin_lock_bh(&mvm->queue_info_lock);
                status = &mvm->queue_info[reserved_txq].status;
                if (WARN((*status != IWL_MVM_QUEUE_RESERVED) &&
                         (*status != IWL_MVM_QUEUE_FREE),
                         "sta_id %d reserved txq %d status %d",
-                        sta_id, reserved_txq, *status)) {
-                       spin_unlock_bh(&mvm->queue_info_lock);
+                        sta_id, reserved_txq, *status))
                        return -EINVAL;
-               }
 
                *status = IWL_MVM_QUEUE_FREE;
-               spin_unlock_bh(&mvm->queue_info_lock);
        }
 
        if (vif->type == NL80211_IFTYPE_STATION &&
@@ -2873,8 +2817,6 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return -EIO;
        }
 
-       spin_lock(&mvm->queue_info_lock);
-
        /*
         * Note the possible cases:
         *  1. An enabled TXQ - TXQ needs to become agg'ed
@@ -2889,7 +2831,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                if (txq_id < 0) {
                        ret = txq_id;
                        IWL_ERR(mvm, "Failed to allocate agg queue\n");
-                       goto release_locks;
+                       goto out;
                }
 
                /* TXQ hasn't yet been enabled, so mark it only as reserved */
@@ -2900,11 +2842,9 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "Can't start tid %d agg on shared queue!\n",
                                    tid);
-               goto release_locks;
+               goto out;
        }
 
-       spin_unlock(&mvm->queue_info_lock);
-
        IWL_DEBUG_TX_QUEUES(mvm,
                            "AGG for tid %d will be on queue #%d\n",
                            tid, txq_id);
@@ -2935,10 +2875,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        ret = 0;
-       goto out;
 
-release_locks:
-       spin_unlock(&mvm->queue_info_lock);
 out:
        spin_unlock_bh(&mvmsta->lock);
 
@@ -3007,9 +2944,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
 
-       spin_lock_bh(&mvm->queue_info_lock);
        queue_status = mvm->queue_info[queue].status;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        /* Maybe there is no need to even alloc a queue... */
        if (mvm->queue_info[queue].status == IWL_MVM_QUEUE_READY)
@@ -3055,9 +2990,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        /* No need to mark as reserved */
-       spin_lock_bh(&mvm->queue_info_lock);
        mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY;
-       spin_unlock_bh(&mvm->queue_info_lock);
 
 out:
        /*
@@ -3083,10 +3016,11 @@ static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
 {
        u16 txq_id = tid_data->txq_id;
 
+       lockdep_assert_held(&mvm->mutex);
+
        if (iwl_mvm_has_new_tx_api(mvm))
                return;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        /*
         * The TXQ is marked as reserved only if no traffic came through yet
         * This means no traffic has been sent on this TID (agg'd or not), so
@@ -3098,8 +3032,6 @@ static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm,
                mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE;
                tid_data->txq_id = IWL_MVM_INVALID_QUEUE;
        }
-
-       spin_unlock_bh(&mvm->queue_info_lock);
 }
 
 int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index de1a0a2..d52cd88 100644 (file)
@@ -397,6 +397,9 @@ struct iwl_mvm_rxq_dup_data {
  * @ptk_pn: per-queue PTK PN data structures
  * @dup_data: per queue duplicate packet detection data
  * @deferred_traffic_tid_map: indication bitmap of deferred traffic per-TID
+ * @tx_ant: the index of the antenna to use for data tx to this station. Only
+ *     used during connection establishment (e.g. for the 4 way handshake
+ *     exchange).
  *
  * When mac80211 creates a station it reserves some space (hw->sta_data_size)
  * in the structure for use by driver. This structure is placed in that
@@ -439,6 +442,7 @@ struct iwl_mvm_sta {
        u8 agg_tids;
        u8 sleep_tx_count;
        u8 avg_energy;
+       u8 tx_ant;
 };
 
 u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data);
index ec57682..09cabf3 100644 (file)
@@ -302,13 +302,30 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                                            offload_assist));
 }
 
+static u32 iwl_mvm_get_tx_ant(struct iwl_mvm *mvm,
+                             struct ieee80211_tx_info *info,
+                             struct ieee80211_sta *sta, __le16 fc)
+{
+       if (info->band == NL80211_BAND_2GHZ &&
+           !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
+               return mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
+
+       if (sta && ieee80211_is_data(fc)) {
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+               return BIT(mvmsta->tx_ant) << RATE_MCS_ANT_POS;
+       }
+
+       return BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+}
+
 static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
                               struct ieee80211_tx_info *info,
                               struct ieee80211_sta *sta)
 {
        int rate_idx;
        u8 rate_plcp;
-       u32 rate_flags;
+       u32 rate_flags = 0;
 
        /* HT rate doesn't make sense for a non data frame */
        WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
@@ -332,13 +349,6 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
        /* Get PLCP rate for tx_cmd->rate_n_flags */
        rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
 
-       if (info->band == NL80211_BAND_2GHZ &&
-           !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
-               rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS;
-       else
-               rate_flags =
-                       BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
-
        /* Set CCK flag as needed */
        if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
                rate_flags |= RATE_MCS_CCK_MSK;
@@ -346,6 +356,14 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
        return (u32)rate_plcp | rate_flags;
 }
 
+static u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm,
+                                      struct ieee80211_tx_info *info,
+                                      struct ieee80211_sta *sta, __le16 fc)
+{
+       return iwl_mvm_get_tx_rate(mvm, info, sta) |
+               iwl_mvm_get_tx_ant(mvm, info, sta, fc);
+}
+
 /*
  * Sets the fields in the Tx cmd that are rate related
  */
@@ -373,20 +391,21 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
         */
 
        if (ieee80211_is_data(fc) && sta) {
-               tx_cmd->initial_rate_index = 0;
-               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
-               return;
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+               if (mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED) {
+                       tx_cmd->initial_rate_index = 0;
+                       tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+                       return;
+               }
        } else if (ieee80211_is_back_req(fc)) {
                tx_cmd->tx_flags |=
                        cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR);
        }
 
-       mvm->mgmt_last_antenna_idx =
-               iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
-                                    mvm->mgmt_last_antenna_idx);
-
        /* Set the rate in the TX cmd */
-       tx_cmd->rate_n_flags = cpu_to_le32(iwl_mvm_get_tx_rate(mvm, info, sta));
+       tx_cmd->rate_n_flags =
+               cpu_to_le32(iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc));
 }
 
 static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info,
@@ -491,6 +510,8 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
                u16 offload_assist = 0;
                u32 rate_n_flags = 0;
                u16 flags = 0;
+               struct iwl_mvm_sta *mvmsta = sta ?
+                       iwl_mvm_sta_from_mac80211(sta) : NULL;
 
                if (ieee80211_is_data_qos(hdr->frame_control)) {
                        u8 *qc = ieee80211_get_qos_ctl(hdr);
@@ -510,10 +531,16 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
                if (!info->control.hw_key)
                        flags |= IWL_TX_FLAGS_ENCRYPT_DIS;
 
-               /* For data packets rate info comes from the fw */
-               if (!(ieee80211_is_data(hdr->frame_control) && sta)) {
+               /*
+                * For data packets rate info comes from the fw. Only
+                * set rate/antenna during connection establishment.
+                */
+               if (sta && (!ieee80211_is_data(hdr->frame_control) ||
+                           mvmsta->sta_state < IEEE80211_STA_AUTHORIZED)) {
                        flags |= IWL_TX_FLAGS_CMD_RATE;
-                       rate_n_flags = iwl_mvm_get_tx_rate(mvm, info, sta);
+                       rate_n_flags =
+                               iwl_mvm_get_tx_rate_n_flags(mvm, info, sta,
+                                                           hdr->frame_control);
                }
 
                if (mvm->trans->cfg->device_family >=
@@ -1160,11 +1187,11 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
                 * If we have timed-out TIDs - schedule the worker that will
                 * reconfig the queues and update them
                 *
-                * Note that the mvm->queue_info_lock isn't being taken here in
-                * order to not serialize the TX flow. This isn't dangerous
-                * because scheduling mvm->add_stream_wk can't ruin the state,
-                * and if we DON'T schedule it due to some race condition then
-                * next TX we get here we will.
+                * Note that the no lock is taken here in order to not serialize
+                * the TX flow. This isn't dangerous because scheduling
+                * mvm->add_stream_wk can't ruin the state, and if we DON'T
+                * schedule it due to some race condition then next TX we get
+                * here we will.
                 */
                if (unlikely(mvm->queue_info[txq_id].status ==
                             IWL_MVM_QUEUE_SHARED &&
@@ -1501,6 +1528,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        break;
                }
 
+               if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS &&
+                   ieee80211_is_mgmt(hdr->frame_control))
+                       iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
+
                /*
                 * If we are freeing multiple frames, mark all the frames
                 * but the first one as acked, since they were acknowledged
@@ -1600,6 +1631,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                iwl_mvm_tx_airtime(mvm, mvmsta,
                                   le16_to_cpu(tx_resp->wireless_media_time));
 
+               if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS &&
+                   mvmsta->sta_state < IEEE80211_STA_AUTHORIZED)
+                       iwl_mvm_toggle_tx_ant(mvm, &mvmsta->tx_ant);
+
                if (sta->wme && tid != IWL_MGMT_TID) {
                        struct iwl_mvm_tid_data *tid_data =
                                &mvmsta->tid_data[tid];
index 818e118..3761fbf 100644 (file)
@@ -285,6 +285,7 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
        return last_idx;
 }
 
+#define FW_SYSASSERT_CPU_MASK 0xf0000000
 static const struct {
        const char *name;
        u8 num;
@@ -301,6 +302,9 @@ static const struct {
        { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
        { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
        { "NMI_INTERRUPT_HOST", 0x66 },
+       { "NMI_INTERRUPT_LMAC_FATAL", 0x70 },
+       { "NMI_INTERRUPT_UMAC_FATAL", 0x71 },
+       { "NMI_INTERRUPT_OTHER_LMAC_FATAL", 0x73 },
        { "NMI_INTERRUPT_ACTION_PT", 0x7C },
        { "NMI_INTERRUPT_UNKNOWN", 0x84 },
        { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
@@ -312,7 +316,7 @@ static const char *desc_lookup(u32 num)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
-               if (advanced_lookup[i].num == num)
+               if (advanced_lookup[i].num == (num & ~FW_SYSASSERT_CPU_MASK))
                        return advanced_lookup[i].name;
 
        /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
@@ -618,13 +622,9 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id,
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -EINVAL;
 
-       spin_lock_bh(&mvm->queue_info_lock);
        if (WARN(mvm->queue_info[queue].tid_bitmap == 0,
-                "Trying to reconfig unallocated queue %d\n", queue)) {
-               spin_unlock_bh(&mvm->queue_info_lock);
+                "Trying to reconfig unallocated queue %d\n", queue))
                return -ENXIO;
-       }
-       spin_unlock_bh(&mvm->queue_info_lock);
 
        IWL_DEBUG_TX_QUEUES(mvm, "Reconfig SCD for TXQ #%d\n", queue);
 
@@ -768,6 +768,29 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
        return result;
 }
 
+void iwl_mvm_send_low_latency_cmd(struct iwl_mvm *mvm,
+                                 bool low_latency, u16 mac_id)
+{
+       struct iwl_mac_low_latency_cmd cmd = {
+               .mac_id = cpu_to_le32(mac_id)
+       };
+
+       if (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))
+               return;
+
+       if (low_latency) {
+               /* currently we don't care about the direction */
+               cmd.low_latency_rx = 1;
+               cmd.low_latency_tx = 1;
+       }
+
+       if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(LOW_LATENCY_CMD,
+                                                MAC_CONF_GROUP, 0),
+                                0, sizeof(cmd), &cmd))
+               IWL_ERR(mvm, "Failed to send low latency command\n");
+}
+
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool low_latency,
                               enum iwl_mvm_low_latency_cause cause)
@@ -786,24 +809,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (low_latency == prev)
                return 0;
 
-       if (fw_has_capa(&mvm->fw->ucode_capa,
-                       IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA)) {
-               struct iwl_mac_low_latency_cmd cmd = {
-                       .mac_id = cpu_to_le32(mvmvif->id)
-               };
-
-               if (low_latency) {
-                       /* currently we don't care about the direction */
-                       cmd.low_latency_rx = 1;
-                       cmd.low_latency_tx = 1;
-               }
-               res = iwl_mvm_send_cmd_pdu(mvm,
-                                          iwl_cmd_id(LOW_LATENCY_CMD,
-                                                     MAC_CONF_GROUP, 0),
-                                          0, sizeof(cmd), &cmd);
-               if (res)
-                       IWL_ERR(mvm, "Failed to send low latency command\n");
-       }
+       iwl_mvm_send_low_latency_cmd(mvm, low_latency, mvmvif->id);
 
        res = iwl_mvm_update_quotas(mvm, false, NULL);
        if (res)
index 9e01521..353581c 100644 (file)
@@ -513,6 +513,56 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)},
 
 /* 9000 Series */
+       {IWL_PCI_DEVICE(0x02F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0038, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0060, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0064, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0230, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0238, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x023C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0260, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x0264, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x02A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x2030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x2034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x40A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x4234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x02F0, 0x42A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0038, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x003C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0060, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0064, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x00A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x00A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0230, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0238, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x023C, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0260, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x0264, iwl9461_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x02A0, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x02A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x1551, iwl9560_killer_s_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x1552, iwl9560_killer_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x2030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x2034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4030, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4034, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x40A4, iwl9462_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x4234, iwl9560_2ac_cfg_soc)},
+       {IWL_PCI_DEVICE(0x06F0, 0x42A4, iwl9462_2ac_cfg_soc)},
        {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x2526, 0x0018, iwl9260_2ac_cfg)},
@@ -832,7 +882,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x34F0, 0x0040, iwl22000_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x34F0, 0x0070, iwl22000_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x34F0, 0x0078, iwl22000_2ax_cfg_hr)},
-       {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ac_cfg_jf)},
+       {IWL_PCI_DEVICE(0x34F0, 0x0310, iwl22000_2ax_cfg_hr)},
        {IWL_PCI_DEVICE(0x40C0, 0x0000, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40C0, 0x0010, iwl22560_2ax_cfg_su_cdb)},
        {IWL_PCI_DEVICE(0x40c0, 0x0090, iwl22560_2ax_cfg_su_cdb)},
index f9c4c64..0f81676 100644 (file)
@@ -378,6 +378,23 @@ struct iwl_tso_hdr_page {
        u8 *pos;
 };
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/**
+ * enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data
+ * debugfs file
+ *
+ * @IWL_FW_MON_DBGFS_STATE_CLOSED: the file is closed.
+ * @IWL_FW_MON_DBGFS_STATE_OPEN: the file is open.
+ * @IWL_FW_MON_DBGFS_STATE_DISABLED: the file is disabled, once this state is
+ *     set the file can no longer be used.
+ */
+enum iwl_fw_mon_dbgfs_state {
+       IWL_FW_MON_DBGFS_STATE_CLOSED,
+       IWL_FW_MON_DBGFS_STATE_OPEN,
+       IWL_FW_MON_DBGFS_STATE_DISABLED,
+};
+#endif
+
 /**
  * enum iwl_shared_irq_flags - level of sharing for irq
  * @IWL_SHARED_IRQ_NON_RX: interrupt vector serves non rx causes.
@@ -415,6 +432,26 @@ struct iwl_self_init_dram {
 };
 
 /**
+ * struct cont_rec: continuous recording data structure
+ * @prev_wr_ptr: the last address that was read in monitor_data
+ *     debugfs file
+ * @prev_wrap_cnt: the wrap count that was used during the last read in
+ *     monitor_data debugfs file
+ * @state: the state of monitor_data debugfs file as described
+ *     in &iwl_fw_mon_dbgfs_state enum
+ * @mutex: locked while reading from monitor_data debugfs file
+ */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+struct cont_rec {
+       u32 prev_wr_ptr;
+       u32 prev_wrap_cnt;
+       u8  state;
+       /* Used to sync monitor_data debugfs file with driver unload flow */
+       struct mutex mutex;
+};
+#endif
+
+/**
  * struct iwl_trans_pcie - PCIe transport specific data
  * @rxq: all the RX queue data
  * @rx_pool: initial pool of iwl_rx_mem_buffer for all the queues
@@ -451,6 +488,9 @@ struct iwl_self_init_dram {
  * @reg_lock: protect hw register access
  * @mutex: to protect stop_device / start_fw / start_hw
  * @cmd_in_flight: true when we have a host command in flight
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ * @fw_mon_data: fw continuous recording data
+#endif
  * @msix_entries: array of MSI-X entries
  * @msix_enabled: true if managed to enable MSI-X
  * @shared_vec_mask: the type of causes the shared vector handles
@@ -538,6 +578,10 @@ struct iwl_trans_pcie {
        bool cmd_hold_nic_awake;
        bool ref_cmd_in_flight;
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct cont_rec fw_mon_data;
+#endif
+
        struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES];
        bool msix_enabled;
        u8 shared_vec_mask;
index 5bafb3f..166bacc 100644 (file)
@@ -71,6 +71,7 @@
 #include <linux/vmalloc.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
+#include <linux/wait.h>
 
 #include "iwl-drv.h"
 #include "iwl-trans.h"
@@ -1729,6 +1730,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
 static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 hpm;
        int err;
 
        lockdep_assert_held(&trans_pcie->mutex);
@@ -1739,6 +1741,17 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
                return err;
        }
 
+       hpm = iwl_trans_read_prph(trans, HPM_DEBUG);
+       if (hpm != 0xa5a5a5a0 && (hpm & PERSISTENCE_BIT)) {
+               if (iwl_trans_read_prph(trans, PREG_PRPH_WPROT_0) &
+                   PREG_WFPM_ACCESS) {
+                       IWL_ERR(trans,
+                               "Error, can not clear persistence bit\n");
+                       return -EPERM;
+               }
+               iwl_trans_write_prph(trans, HPM_DEBUG, hpm & ~PERSISTENCE_BIT);
+       }
+
        iwl_trans_pcie_sw_reset(trans);
 
        err = iwl_pcie_apm_init(trans);
@@ -2697,6 +2710,137 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
        return count;
 }
 
+static int iwl_dbgfs_monitor_data_open(struct inode *inode,
+                                      struct file *file)
+{
+       struct iwl_trans *trans = inode->i_private;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       if (!trans->dbg_dest_tlv ||
+           trans->dbg_dest_tlv->monitor_mode != EXTERNAL_MODE) {
+               IWL_ERR(trans, "Debug destination is not set to DRAM\n");
+               return -ENOENT;
+       }
+
+       if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED)
+               return -EBUSY;
+
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN;
+       return simple_open(inode, file);
+}
+
+static int iwl_dbgfs_monitor_data_release(struct inode *inode,
+                                         struct file *file)
+{
+       struct iwl_trans_pcie *trans_pcie =
+               IWL_TRANS_GET_PCIE_TRANS(inode->i_private);
+
+       if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN)
+               trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       return 0;
+}
+
+static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,
+                                 void *buf, ssize_t *size,
+                                 ssize_t *bytes_copied)
+{
+       int buf_size_left = count - *bytes_copied;
+
+       buf_size_left = buf_size_left - (buf_size_left % sizeof(u32));
+       if (*size > buf_size_left)
+               *size = buf_size_left;
+
+       *size -= copy_to_user(user_buf, buf, *size);
+       *bytes_copied += *size;
+
+       if (buf_size_left == *size)
+               return true;
+       return false;
+}
+
+static ssize_t iwl_dbgfs_monitor_data_read(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct iwl_trans *trans = file->private_data;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       void *cpu_addr = (void *)trans->fw_mon[0].block, *curr_buf;
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+       u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt;
+       ssize_t size, bytes_copied = 0;
+       bool b_full;
+
+       if (trans->dbg_dest_tlv) {
+               write_ptr_addr =
+                       le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+               wrap_cnt_addr = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+       } else {
+               write_ptr_addr = MON_BUFF_WRPTR;
+               wrap_cnt_addr = MON_BUFF_CYCLE_CNT;
+       }
+
+       if (unlikely(!trans->dbg_rec_on))
+               return 0;
+
+       mutex_lock(&data->mutex);
+       if (data->state ==
+           IWL_FW_MON_DBGFS_STATE_DISABLED) {
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+
+       /* write_ptr position in bytes rather then DW */
+       write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32);
+       wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr);
+
+       if (data->prev_wrap_cnt == wrap_cnt) {
+               size = write_ptr - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+       } else if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                  write_ptr < data->prev_wr_ptr) {
+               size = trans->fw_mon[0].size - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+               if (!b_full) {
+                       size = write_ptr;
+                       b_full = iwl_write_to_user_buf(user_buf, count,
+                                                      cpu_addr, &size,
+                                                      &bytes_copied);
+                       data->prev_wr_ptr = size;
+                       data->prev_wrap_cnt++;
+               }
+       } else {
+               if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                   write_ptr > data->prev_wr_ptr)
+                       IWL_WARN(trans,
+                                "write pointer passed previous write pointer, start copying from the beginning\n");
+               else if (!unlikely(data->prev_wrap_cnt == 0 &&
+                                  data->prev_wr_ptr == 0))
+                       IWL_WARN(trans,
+                                "monitor data is out of sync, start copying from the beginning\n");
+
+               size = write_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              cpu_addr, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr = size;
+               data->prev_wrap_cnt = wrap_cnt;
+       }
+
+       mutex_unlock(&data->mutex);
+
+       return bytes_copied;
+}
+
 DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
 DEBUGFS_READ_FILE_OPS(fh_reg);
 DEBUGFS_READ_FILE_OPS(rx_queue);
@@ -2704,6 +2848,12 @@ DEBUGFS_READ_FILE_OPS(tx_queue);
 DEBUGFS_WRITE_FILE_OPS(csr);
 DEBUGFS_READ_WRITE_FILE_OPS(rfkill);
 
+static const struct file_operations iwl_dbgfs_monitor_data_ops = {
+       .read = iwl_dbgfs_monitor_data_read,
+       .open = iwl_dbgfs_monitor_data_open,
+       .release = iwl_dbgfs_monitor_data_release,
+};
+
 /* Create the debugfs files and directories */
 int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
 {
@@ -2715,12 +2865,23 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
        DEBUGFS_ADD_FILE(csr, dir, 0200);
        DEBUGFS_ADD_FILE(fh_reg, dir, 0400);
        DEBUGFS_ADD_FILE(rfkill, dir, 0600);
+       DEBUGFS_ADD_FILE(monitor_data, dir, 0400);
        return 0;
 
 err:
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
 }
+
+static void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+
+       mutex_lock(&data->mutex);
+       data->state = IWL_FW_MON_DBGFS_STATE_DISABLED;
+       mutex_unlock(&data->mutex);
+}
 #endif /*CONFIG_IWLWIFI_DEBUGFS */
 
 static u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd)
@@ -2978,7 +3139,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len)
 
 static struct iwl_trans_dump_data
 *iwl_trans_pcie_dump_data(struct iwl_trans *trans,
-                         const struct iwl_fw_dbg_trigger_tlv *trigger)
+                         u32 dump_mask)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_fw_error_dump_data *data;
@@ -2990,7 +3151,10 @@ static struct iwl_trans_dump_data
        int i, ptr;
        bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) &&
                        !trans->cfg->mq_rx_supported &&
-                       trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
+                       dump_mask & BIT(IWL_FW_ERROR_DUMP_RB);
+
+       if (!dump_mask)
+               return NULL;
 
        /* transport dump header */
        len = sizeof(*dump_data);
@@ -3002,11 +3166,7 @@ static struct iwl_trans_dump_data
        /* FW monitor */
        monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);
 
-       if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {
-               if (!(trans->dbg_dump_mask &
-                     BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)))
-                       return NULL;
-
+       if (dump_mask == BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) {
                dump_data = vzalloc(len);
                if (!dump_data)
                        return NULL;
@@ -3019,11 +3179,11 @@ static struct iwl_trans_dump_data
        }
 
        /* CSR registers */
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
                len += sizeof(*data) + IWL_CSR_TO_DUMP;
 
        /* FH registers */
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) {
                if (trans->cfg->gen2)
                        len += sizeof(*data) +
                               (FH_MEM_UPPER_BOUND_GEN2 -
@@ -3048,8 +3208,7 @@ static struct iwl_trans_dump_data
        }
 
        /* Paged memory for gen2 HW */
-       if (trans->cfg->gen2 &&
-           trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
+       if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING))
                for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++)
                        len += sizeof(*data) +
                               sizeof(struct iwl_fw_error_dump_paging) +
@@ -3062,7 +3221,7 @@ static struct iwl_trans_dump_data
        len = 0;
        data = (void *)dump_data->data;
 
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) {
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) {
                u16 tfd_size = trans_pcie->tfd_size;
 
                data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
@@ -3096,16 +3255,15 @@ static struct iwl_trans_dump_data
                data = iwl_fw_error_next_data(data);
        }
 
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR))
                len += iwl_trans_pcie_dump_csr(trans, &data);
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS))
                len += iwl_trans_pcie_fh_regs_dump(trans, &data);
        if (dump_rbs)
                len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
 
        /* Paged memory for gen2 HW */
-       if (trans->cfg->gen2 &&
-           trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
+       if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) {
                for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) {
                        struct iwl_fw_error_dump_paging *paging;
                        dma_addr_t addr =
@@ -3125,7 +3283,7 @@ static struct iwl_trans_dump_data
                        len += sizeof(*data) + sizeof(*paging) + page_len;
                }
        }
-       if (trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
+       if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR))
                len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
 
        dump_data->len = len;
@@ -3202,6 +3360,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
 
        .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
        .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
@@ -3221,6 +3382,9 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
        .txq_free = iwl_trans_pcie_dyn_txq_free,
        .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
        .rxq_dma_data = iwl_trans_pcie_rxq_dma_data,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
@@ -3392,8 +3556,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 #if IS_ENABLED(CONFIG_IWLMVM)
        trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
 
-       if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
-           CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
+       if (cfg == &iwl22000_2ax_cfg_hr) {
+               if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+                   CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
+                       trans->cfg = &iwl22000_2ax_cfg_hr;
+               } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+                          CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_JF)) {
+                       trans->cfg = &iwl22000_2ax_cfg_jf;
+               } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+                          CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HRCDB)) {
+                       IWL_ERR(trans, "RF ID HRCDB is not supported\n");
+                       ret = -EINVAL;
+                       goto out_no_pci;
+               } else {
+                       IWL_ERR(trans, "Unrecognized RF ID 0x%08x\n",
+                               CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id));
+                       ret = -EINVAL;
+                       goto out_no_pci;
+               }
+       } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) ==
+                  CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR)) {
                u32 hw_status;
 
                hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS);
@@ -3454,6 +3636,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
 #endif /* CONFIG_IWLWIFI_PCIE_RTPM */
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       mutex_init(&trans_pcie->fw_mon_data.mutex);
+#endif
+
        return trans;
 
 out_free_ict:
index e880f69..0c5f11d 100644 (file)
@@ -1228,8 +1228,7 @@ int iwl_trans_pcie_txq_alloc_response(struct iwl_trans *trans,
        /* Place first TFD at index corresponding to start sequence number */
        txq->read_ptr = wr_ptr;
        txq->write_ptr = wr_ptr;
-       iwl_write_direct32(trans, HBUS_TARG_WRPTR,
-                          (txq->write_ptr) | (qid << 16));
+
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid);
 
        iwl_free_resp(hcmd);
index 012930d..b0e7c0a 100644 (file)
@@ -690,7 +690,7 @@ static int prism2_open(struct net_device *dev)
                /* Master radio interface is needed for all operation, so open
                 * it automatically when any virtual net_device is opened. */
                local->master_dev_auto_open = 1;
-               dev_open(local->dev);
+               dev_open(local->dev, NULL);
        }
 
        netif_device_attach(dev);
index 21bb684..40a8b94 100644 (file)
@@ -908,6 +908,7 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
        case EZUSB_CTX_REQ_SUBMITTED:
                if (!ctx->in_rid)
                        break;
+               /* fall through */
        default:
                err("%s: Unexpected context state %d", __func__,
                    state);
index ce9d4db..b0eb58a 100644 (file)
@@ -235,6 +235,7 @@ isl38xx_in_queue(isl38xx_control_block *cb, int queue)
                /* send queues */
        case ISL38XX_CB_TX_MGMTQ:
                BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
+               /* fall through */
 
        case ISL38XX_CB_TX_DATA_LQ:
        case ISL38XX_CB_TX_DATA_HQ:
index 334717b..3ccf2a4 100644 (file)
@@ -1691,6 +1691,7 @@ static int prism54_get_encodeext(struct net_device *ndev,
        case DOT11_AUTH_BOTH:
        case DOT11_AUTH_SK:
                wrqu->encoding.flags |= IW_ENCODE_RESTRICTED;
+               /* fall through */
        case DOT11_AUTH_OS:
        default:
                wrqu->encoding.flags |= IW_ENCODE_OPEN;
index 325176d..ad6d3a5 100644 (file)
@@ -932,6 +932,7 @@ islpci_set_state(islpci_private *priv, islpci_state_t new_state)
        switch (new_state) {
        case PRV_STATE_OFF:
                priv->state_off++;
+               /* fall through */
        default:
                priv->state = new_state;
                break;
index 504d6e0..7c3224b 100644 (file)
@@ -796,15 +796,13 @@ static void if_spi_h2c(struct if_spi_card *card,
 {
        struct lbs_private *priv = card->priv;
        int err = 0;
-       u16 int_type, port_reg;
+       u16 port_reg;
 
        switch (type) {
        case MVMS_DAT:
-               int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
                port_reg = IF_SPI_DATA_RDWRPORT_REG;
                break;
        case MVMS_CMD:
-               int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
                port_reg = IF_SPI_CMD_RDWRPORT_REG;
                break;
        default:
index 75cbd60..6845eb5 100644 (file)
@@ -363,6 +363,7 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
                                                    (const u8 *)hdr,
                                                    hdr->len + sizeof(struct ieee_types_header)))
                                break;
+                       /* fall through */
                default:
                        memcpy(gen_ie->ie_buffer + ie_len, hdr,
                               hdr->len + sizeof(struct ieee_types_header));
index b8c12a5..6cf5202 100644 (file)
@@ -1,11 +1,11 @@
 config QTNFMAC
        tristate
-       depends on QTNFMAC_PEARL_PCIE
-       default m if QTNFMAC_PEARL_PCIE=m
-       default y if QTNFMAC_PEARL_PCIE=y
+       depends on QTNFMAC_PCIE
+       default m if QTNFMAC_PCIE=m
+       default y if QTNFMAC_PCIE=y
 
-config QTNFMAC_PEARL_PCIE
-       tristate "Quantenna QSR10g PCIe support"
+config QTNFMAC_PCIE
+       tristate "Quantenna QSR1000/QSR2000/QSR10g PCIe support"
        default n
        depends on PCI && CFG80211
        select QTNFMAC
@@ -13,7 +13,8 @@ config QTNFMAC_PEARL_PCIE
        select CRC32
        help
          This option adds support for wireless adapters based on Quantenna
-         802.11ac QSR10g (aka Pearl) FullMAC chipset running over PCIe.
+         802.11ac QSR10g (aka Pearl) and QSR1000/QSR2000 (aka Topaz)
+         FullMAC chipsets running over PCIe.
 
          If you choose to build it as a module, two modules will be built:
-         qtnfmac.ko and qtnfmac_pearl_pcie.ko.
+         qtnfmac.ko and qtnfmac_pcie.ko.
index 17cd7ad..40dffbd 100644 (file)
@@ -19,11 +19,12 @@ qtnfmac-objs += \
 
 #
 
-obj-$(CONFIG_QTNFMAC_PEARL_PCIE) += qtnfmac_pearl_pcie.o
+obj-$(CONFIG_QTNFMAC_PCIE) += qtnfmac_pcie.o
 
-qtnfmac_pearl_pcie-objs += \
+qtnfmac_pcie-objs += \
        shm_ipc.o \
        pcie/pcie.o \
-       pcie/pearl_pcie.o
+       pcie/pearl_pcie.o \
+       pcie/topaz_pcie.o
 
-qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o
+qtnfmac_pcie-$(CONFIG_DEBUG_FS) += debug.o
index bfdc1ad..659e764 100644 (file)
@@ -84,7 +84,7 @@ static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
                                    size_t *var_resp_size)
 {
        struct qlink_cmd *cmd;
-       const struct qlink_resp *resp;
+       struct qlink_resp *resp = NULL;
        struct sk_buff *resp_skb = NULL;
        u16 cmd_id;
        u8 mac_id;
@@ -113,7 +113,12 @@ static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus,
        if (ret)
                goto out;
 
-       resp = (const struct qlink_resp *)resp_skb->data;
+       if (WARN_ON(!resp_skb || !resp_skb->data)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       resp = (struct qlink_resp *)resp_skb->data;
        ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id,
                                          const_resp_size);
        if (ret)
@@ -686,7 +691,7 @@ int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        struct qlink_cmd_get_sta_info *cmd;
        const struct qlink_resp_get_sta_info *resp;
-       size_t var_resp_len;
+       size_t var_resp_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
@@ -1650,7 +1655,7 @@ int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        const struct qlink_resp_get_mac_info *resp;
-       size_t var_data_len;
+       size_t var_data_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
@@ -1680,8 +1685,8 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        const struct qlink_resp_get_hw_info *resp;
+       size_t info_len = 0;
        int ret = 0;
-       size_t info_len;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
                                            QLINK_CMD_GET_HW_INFO,
@@ -1709,9 +1714,9 @@ int qtnf_cmd_band_info_get(struct qtnf_wmac *mac,
                           struct ieee80211_supported_band *band)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
-       size_t info_len;
        struct qlink_cmd_band_info_get *cmd;
        struct qlink_resp_band_info_get *resp;
+       size_t info_len = 0;
        int ret = 0;
        u8 qband;
 
@@ -1764,8 +1769,8 @@ out:
 int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac)
 {
        struct sk_buff *cmd_skb, *resp_skb = NULL;
-       size_t response_size;
        struct qlink_resp_phy_params *resp;
+       size_t response_size = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0,
@@ -2431,7 +2436,7 @@ int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
        struct sk_buff *cmd_skb, *resp_skb = NULL;
        struct qlink_cmd_get_chan_stats *cmd;
        struct qlink_resp_get_chan_stats *resp;
-       size_t var_data_len;
+       size_t var_data_len = 0;
        int ret = 0;
 
        cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
index 16795db..c3a32ef 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */
 
+#include <linux/module.h>
 #include <linux/printk.h>
 #include <linux/pci.h>
 #include <linux/spinlock.h>
 #include "shm_ipc.h"
 #include "core.h"
 #include "debug.h"
-
-#undef pr_fmt
-#define pr_fmt(fmt)    "qtnf_pcie: %s: " fmt, __func__
+#include "util.h"
+#include "qtn_hw_ids.h"
 
 #define QTN_SYSCTL_BAR 0
 #define QTN_SHMEM_BAR  2
 #define QTN_DMA_BAR    3
 
+#define QTN_PCIE_MAX_FW_BUFSZ          (1 * 1024 * 1024)
+
+static bool use_msi = true;
+module_param(use_msi, bool, 0644);
+MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
+
+static unsigned int tx_bd_size_param;
+module_param(tx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size");
+
+static unsigned int rx_bd_size_param = 256;
+module_param(rx_bd_size_param, uint, 0644);
+MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size");
+
+static u8 flashboot = 1;
+module_param(flashboot, byte, 0644);
+MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
+
+static unsigned int fw_blksize_param = QTN_PCIE_MAX_FW_BUFSZ;
+module_param(fw_blksize_param, uint, 0644);
+MODULE_PARM_DESC(fw_blksize_param, "firmware loading block size in bytes");
+
+#define DRV_NAME       "qtnfmac_pcie"
+
 int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
@@ -58,7 +82,7 @@ int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv)
        return 0;
 }
 
-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
+static void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
        struct pci_dev *pdev = priv->pdev;
@@ -72,7 +96,7 @@ static int qtnf_dbg_mps_show(struct seq_file *s, void *data)
        struct qtnf_bus *bus = dev_get_drvdata(s->private);
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
 
-       seq_printf(s, "%d\n", priv->mps);
+       seq_printf(s, "%d\n", pcie_get_mps(priv->pdev));
 
        return 0;
 }
@@ -104,8 +128,7 @@ static int qtnf_dbg_shm_stats(struct seq_file *s, void *data)
        return 0;
 }
 
-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
-                           const char *drv_name)
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success)
 {
        struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus);
        struct pci_dev *pdev = priv->pdev;
@@ -122,7 +145,7 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
        }
 
        if (boot_success) {
-               qtnf_debugfs_init(bus, drv_name);
+               qtnf_debugfs_init(bus, DRV_NAME);
                qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show);
                qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show);
                qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats);
@@ -133,9 +156,8 @@ void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
        put_device(&pdev->dev);
 }
 
-static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
+static void qtnf_tune_pcie_mps(struct pci_dev *pdev)
 {
-       struct pci_dev *pdev = priv->pdev;
        struct pci_dev *parent;
        int mps_p, mps_o, mps_m, mps;
        int ret;
@@ -163,12 +185,10 @@ static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv)
        if (ret) {
                pr_err("failed to set mps to %d, keep using current %d\n",
                       mps, mps_o);
-               priv->mps = mps_o;
                return;
        }
 
        pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m);
-       priv->mps = mps;
 }
 
 static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
@@ -194,20 +214,20 @@ static void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi)
        }
 }
 
-static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
+static void __iomem *qtnf_map_bar(struct pci_dev *pdev, u8 index)
 {
        void __iomem *vaddr;
        dma_addr_t busaddr;
        size_t len;
        int ret;
 
-       ret = pcim_iomap_regions(priv->pdev, 1 << index, "qtnfmac_pcie");
+       ret = pcim_iomap_regions(pdev, 1 << index, "qtnfmac_pcie");
        if (ret)
                return IOMEM_ERR_PTR(ret);
 
-       busaddr = pci_resource_start(priv->pdev, index);
-       len = pci_resource_len(priv->pdev, index);
-       vaddr = pcim_iomap_table(priv->pdev)[index];
+       busaddr = pci_resource_start(pdev, index);
+       len = pci_resource_len(pdev, index);
+       vaddr = pcim_iomap_table(pdev)[index];
        if (!vaddr)
                return IOMEM_ERR_PTR(-ENOMEM);
 
@@ -217,31 +237,6 @@ static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index)
        return vaddr;
 }
 
-static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv)
-{
-       int ret = -ENOMEM;
-
-       priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR);
-       if (IS_ERR(priv->sysctl_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
-               return ret;
-       }
-
-       priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR);
-       if (IS_ERR(priv->dmareg_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
-               return ret;
-       }
-
-       priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR);
-       if (IS_ERR(priv->epmem_bar)) {
-               pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
-               return ret;
-       }
-
-       return 0;
-}
-
 static void qtnf_pcie_control_rx_callback(void *arg, const u8 __iomem *buf,
                                          size_t len)
 {
@@ -282,27 +277,83 @@ void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
                          ipc_int, &rx_callback);
 }
 
-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
-                   const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
-                   bool use_msi)
+static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct qtnf_pcie_bus_priv *pcie_priv;
        struct qtnf_bus *bus;
+       void __iomem *sysctl_bar;
+       void __iomem *epmem_bar;
+       void __iomem *dmareg_bar;
+       unsigned int chipid;
        int ret;
 
-       bus = devm_kzalloc(&pdev->dev,
-                          sizeof(*bus) + priv_size, GFP_KERNEL);
+       if (!pci_is_pcie(pdev)) {
+               pr_err("device %s is not PCI Express\n", pci_name(pdev));
+               return -EIO;
+       }
+
+       qtnf_tune_pcie_mps(pdev);
+
+       ret = pcim_enable_device(pdev);
+       if (ret) {
+               pr_err("failed to init PCI device %x\n", pdev->device);
+               return ret;
+       }
+
+       pci_set_master(pdev);
+
+       sysctl_bar = qtnf_map_bar(pdev, QTN_SYSCTL_BAR);
+       if (IS_ERR(sysctl_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR);
+               return ret;
+       }
+
+       dmareg_bar = qtnf_map_bar(pdev, QTN_DMA_BAR);
+       if (IS_ERR(dmareg_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_DMA_BAR);
+               return ret;
+       }
+
+       epmem_bar = qtnf_map_bar(pdev, QTN_SHMEM_BAR);
+       if (IS_ERR(epmem_bar)) {
+               pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR);
+               return ret;
+       }
+
+       chipid = qtnf_chip_id_get(sysctl_bar);
+
+       pr_info("identified device: %s\n", qtnf_chipid_to_string(chipid));
+
+       switch (chipid) {
+       case QTN_CHIP_ID_PEARL:
+       case QTN_CHIP_ID_PEARL_B:
+       case QTN_CHIP_ID_PEARL_C:
+               bus = qtnf_pcie_pearl_alloc(pdev);
+               break;
+       case QTN_CHIP_ID_TOPAZ:
+               bus = qtnf_pcie_topaz_alloc(pdev);
+               break;
+       default:
+               pr_err("unsupported chip ID 0x%x\n", chipid);
+               return -ENOTSUPP;
+       }
+
        if (!bus)
                return -ENOMEM;
 
        pcie_priv = get_bus_priv(bus);
-
        pci_set_drvdata(pdev, bus);
-       bus->bus_ops = bus_ops;
        bus->dev = &pdev->dev;
        bus->fw_state = QTNF_FW_STATE_RESET;
        pcie_priv->pdev = pdev;
        pcie_priv->tx_stopped = 0;
+       pcie_priv->rx_bd_num = rx_bd_size_param;
+       pcie_priv->flashboot = flashboot;
+
+       if (fw_blksize_param > QTN_PCIE_MAX_FW_BUFSZ)
+               pcie_priv->fw_blksize =  QTN_PCIE_MAX_FW_BUFSZ;
+       else
+               pcie_priv->fw_blksize = fw_blksize_param;
 
        mutex_init(&bus->bus_lock);
        spin_lock_init(&pcie_priv->tx_lock);
@@ -317,53 +368,35 @@ int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
        pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE");
        if (!pcie_priv->workqueue) {
                pr_err("failed to alloc bus workqueue\n");
-               ret = -ENODEV;
-               goto err_init;
-       }
-
-       init_dummy_netdev(&bus->mux_dev);
-
-       if (!pci_is_pcie(pdev)) {
-               pr_err("device %s is not PCI Express\n", pci_name(pdev));
-               ret = -EIO;
-               goto err_base;
-       }
-
-       qtnf_tune_pcie_mps(pcie_priv);
-
-       ret = pcim_enable_device(pdev);
-       if (ret) {
-               pr_err("failed to init PCI device %x\n", pdev->device);
-               goto err_base;
-       } else {
-               pr_debug("successful init of PCI device %x\n", pdev->device);
+               return -ENODEV;
        }
 
-       ret = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
+       ret = dma_set_mask_and_coherent(&pdev->dev,
+                                       pcie_priv->dma_mask_get_cb());
        if (ret) {
-               pr_err("PCIE DMA coherent mask init failed\n");
-               goto err_base;
+               pr_err("PCIE DMA coherent mask init failed 0x%llx\n",
+                      pcie_priv->dma_mask_get_cb());
+               goto error;
        }
 
-       pci_set_master(pdev);
+       init_dummy_netdev(&bus->mux_dev);
        qtnf_pcie_init_irq(pcie_priv, use_msi);
-
-       ret = qtnf_pcie_init_memory(pcie_priv);
-       if (ret < 0) {
-               pr_err("PCIE memory init failed\n");
-               goto err_base;
-       }
-
+       pcie_priv->sysctl_bar = sysctl_bar;
+       pcie_priv->dmareg_bar = dmareg_bar;
+       pcie_priv->epmem_bar = epmem_bar;
        pci_save_state(pdev);
 
+       ret = pcie_priv->probe_cb(bus, tx_bd_size_param);
+       if (ret)
+               goto error;
+
+       qtnf_pcie_bringup_fw_async(bus);
        return 0;
 
-err_base:
+error:
        flush_workqueue(pcie_priv->workqueue);
        destroy_workqueue(pcie_priv->workqueue);
-err_init:
        pci_set_drvdata(pdev, NULL);
-
        return ret;
 }
 
@@ -373,8 +406,17 @@ static void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv)
        qtnf_shm_ipc_free(&priv->shm_ipc_ep_out);
 }
 
-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)
+static void qtnf_pcie_remove(struct pci_dev *dev)
 {
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(dev);
+       if (!bus)
+               return;
+
+       priv = get_bus_priv(bus);
+
        cancel_work_sync(&bus->fw_work);
 
        if (bus->fw_state == QTNF_FW_STATE_ACTIVE ||
@@ -388,5 +430,77 @@ void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv)
 
        qtnf_pcie_free_shm_ipc(priv);
        qtnf_debugfs_remove(bus);
+       priv->remove_cb(bus);
        pci_set_drvdata(priv->pdev, NULL);
 }
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_suspend(struct device *dev)
+{
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(to_pci_dev(dev));
+       if (!bus)
+               return -EFAULT;
+
+       priv = get_bus_priv(bus);
+       return priv->suspend_cb(bus);
+}
+
+static int qtnf_pcie_resume(struct device *dev)
+{
+       struct qtnf_pcie_bus_priv *priv;
+       struct qtnf_bus *bus;
+
+       bus = pci_get_drvdata(to_pci_dev(dev));
+       if (!bus)
+               return -EFAULT;
+
+       priv = get_bus_priv(bus);
+       return priv->resume_cb(bus);
+}
+
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend,
+                        qtnf_pcie_resume);
+#endif
+
+static const struct pci_device_id qtnf_pcie_devid_table[] = {
+       {
+               PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QSR,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+       },
+       { },
+};
+
+MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+
+static struct pci_driver qtnf_pcie_drv_data = {
+       .name = DRV_NAME,
+       .id_table = qtnf_pcie_devid_table,
+       .probe = qtnf_pcie_probe,
+       .remove = qtnf_pcie_remove,
+#ifdef CONFIG_PM_SLEEP
+       .driver = {
+               .pm = &qtnf_pcie_pm_ops,
+       },
+#endif
+};
+
+static int __init qtnf_pcie_register(void)
+{
+       return pci_register_driver(&qtnf_pcie_drv_data);
+}
+
+static void __exit qtnf_pcie_exit(void)
+{
+       pci_unregister_driver(&qtnf_pcie_drv_data);
+}
+
+module_init(qtnf_pcie_register);
+module_exit(qtnf_pcie_exit);
+
+MODULE_AUTHOR("Quantenna Communications");
+MODULE_DESCRIPTION("Quantenna PCIe bus driver for 802.11 wireless LAN.");
+MODULE_LICENSE("GPL");
index 5c70fb4..bbc074e 100644 (file)
 struct qtnf_pcie_bus_priv {
        struct pci_dev *pdev;
 
+       int (*probe_cb)(struct qtnf_bus *bus, unsigned int tx_bd_size);
+       void (*remove_cb)(struct qtnf_bus *bus);
+       int (*suspend_cb)(struct qtnf_bus *bus);
+       int (*resume_cb)(struct qtnf_bus *bus);
+       u64 (*dma_mask_get_cb)(void);
+
        spinlock_t tx_reclaim_lock;
        spinlock_t tx_lock;
-       int mps;
 
        struct workqueue_struct *workqueue;
        struct tasklet_struct reclaim_tq;
@@ -43,6 +48,8 @@ struct qtnf_pcie_bus_priv {
        struct sk_buff **tx_skb;
        struct sk_buff **rx_skb;
 
+       unsigned int fw_blksize;
+
        u32 rx_bd_w_index;
        u32 rx_bd_r_index;
 
@@ -58,21 +65,18 @@ struct qtnf_pcie_bus_priv {
 
        u8 msi_enabled;
        u8 tx_stopped;
+       bool flashboot;
 };
 
 int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb);
 int qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv);
-void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus);
-void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success,
-                           const char *drv_name);
+void qtnf_pcie_fw_boot_done(struct qtnf_bus *bus, bool boot_success);
 void qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv,
                            struct qtnf_shm_ipc_region __iomem *ipc_tx_reg,
                            struct qtnf_shm_ipc_region __iomem *ipc_rx_reg,
                            const struct qtnf_shm_ipc_int *ipc_int);
-int qtnf_pcie_probe(struct pci_dev *pdev, size_t priv_size,
-                   const struct qtnf_bus_ops *bus_ops, u64 dma_mask,
-                   bool use_msi);
-void qtnf_pcie_remove(struct qtnf_bus *bus, struct qtnf_pcie_bus_priv *priv);
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev);
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev);
 
 static inline void qtnf_non_posted_write(u32 val, void __iomem *basereg)
 {
index 95c7b95..1f5facb 100644 (file)
@@ -2,7 +2,6 @@
 /* Copyright (c) 2018 Quantenna Communications */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include "shm_ipc.h"
 #include "debug.h"
 
-static bool use_msi = true;
-module_param(use_msi, bool, 0644);
-MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt");
-
-static unsigned int tx_bd_size_param = 32;
-module_param(tx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two");
-
-static unsigned int rx_bd_size_param = 256;
-module_param(rx_bd_size_param, uint, 0644);
-MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two");
-
-static u8 flashboot = 1;
-module_param(flashboot, byte, 0644);
-MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
-
-#define DRV_NAME       "qtnfmac_pearl_pcie"
+#define PEARL_TX_BD_SIZE_DEFAULT       32
 
 struct qtnf_pearl_bda {
        __le16 bda_len;
@@ -415,30 +398,28 @@ static int pearl_hhbm_init(struct qtnf_pcie_pearl_state *ps)
        return 0;
 }
 
-static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps)
+static int qtnf_pcie_pearl_init_xfer(struct qtnf_pcie_pearl_state *ps,
+                                    unsigned int tx_bd_size)
 {
        struct qtnf_pcie_bus_priv *priv = &ps->base;
        int ret;
        u32 val;
 
-       priv->tx_bd_num = tx_bd_size_param;
-       priv->rx_bd_num = rx_bd_size_param;
-       priv->rx_bd_w_index = 0;
-       priv->rx_bd_r_index = 0;
+       if (tx_bd_size == 0)
+               tx_bd_size = PEARL_TX_BD_SIZE_DEFAULT;
 
-       if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) {
-               pr_err("tx_bd_size_param %u is not power of two\n",
-                      priv->tx_bd_num);
-               return -EINVAL;
-       }
+       val = tx_bd_size * sizeof(struct qtnf_pearl_tx_bd);
 
-       val = priv->tx_bd_num * sizeof(struct qtnf_pearl_tx_bd);
-       if (val > PCIE_HHBM_MAX_SIZE) {
-               pr_err("tx_bd_size_param %u is too large\n",
-                      priv->tx_bd_num);
-               return -EINVAL;
+       if (!is_power_of_2(tx_bd_size) || val > PCIE_HHBM_MAX_SIZE) {
+               pr_warn("bad tx_bd_size value %u\n", tx_bd_size);
+               priv->tx_bd_num = PEARL_TX_BD_SIZE_DEFAULT;
+       } else {
+               priv->tx_bd_num = tx_bd_size;
        }
 
+       priv->rx_bd_w_index = 0;
+       priv->rx_bd_r_index = 0;
+
        if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) {
                pr_err("rx_bd_size_param %u is not power of two\n",
                       priv->rx_bd_num);
@@ -1006,7 +987,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
        const char *fwname = QTN_PCI_PEARL_FW_NAME;
        bool fw_boot_success = false;
 
-       if (flashboot) {
+       if (ps->base.flashboot) {
                state |= QTN_RC_FW_FLASHBOOT;
        } else {
                ret = request_firmware(&fw, fwname, &pdev->dev);
@@ -1022,7 +1003,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
                            QTN_FW_DL_TIMEOUT_MS)) {
                pr_err("card is not ready\n");
 
-               if (!flashboot)
+               if (!ps->base.flashboot)
                        release_firmware(fw);
 
                goto fw_load_exit;
@@ -1030,7 +1011,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
 
        qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY);
 
-       if (flashboot) {
+       if (ps->base.flashboot) {
                pr_info("booting firmware from flash\n");
 
        } else {
@@ -1061,7 +1042,7 @@ static void qtnf_pearl_fw_work_handler(struct work_struct *work)
        fw_boot_success = true;
 
 fw_load_exit:
-       qtnf_pcie_fw_boot_done(bus, fw_boot_success, DRV_NAME);
+       qtnf_pcie_fw_boot_done(bus, fw_boot_success);
 
        if (fw_boot_success) {
                qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats);
@@ -1077,74 +1058,34 @@ static void qtnf_pearl_reclaim_tasklet_fn(unsigned long data)
        qtnf_en_txdone_irq(ps);
 }
 
-static int qtnf_pearl_check_chip_id(struct qtnf_pcie_pearl_state *ps)
+static u64 qtnf_pearl_dma_mask_get(void)
 {
-       unsigned int chipid;
-
-       chipid = qtnf_chip_id_get(ps->base.sysctl_bar);
-
-       switch (chipid) {
-       case QTN_CHIP_ID_PEARL:
-       case QTN_CHIP_ID_PEARL_B:
-       case QTN_CHIP_ID_PEARL_C:
-               pr_info("chip ID is 0x%x\n", chipid);
-               break;
-       default:
-               pr_err("incorrect chip ID 0x%x\n", chipid);
-               return -ENODEV;
-       }
-
-       return 0;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+       return DMA_BIT_MASK(64);
+#else
+       return DMA_BIT_MASK(32);
+#endif
 }
 
-static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,
-                                const struct pci_device_id *id)
+static int qtnf_pcie_pearl_probe(struct qtnf_bus *bus, unsigned int tx_bd_size)
 {
        struct qtnf_shm_ipc_int ipc_int;
-       struct qtnf_pcie_pearl_state *ps;
-       struct qtnf_bus *bus;
+       struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);
+       struct pci_dev *pdev = ps->base.pdev;
        int ret;
-       u64 dma_mask;
-
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-       dma_mask = DMA_BIT_MASK(64);
-#else
-       dma_mask = DMA_BIT_MASK(32);
-#endif
-
-       ret = qtnf_pcie_probe(pdev, sizeof(*ps), &qtnf_pcie_pearl_bus_ops,
-                             dma_mask, use_msi);
-       if (ret)
-               return ret;
-
-       bus = pci_get_drvdata(pdev);
-       ps = get_bus_priv(bus);
 
+       bus->bus_ops = &qtnf_pcie_pearl_bus_ops;
        spin_lock_init(&ps->irq_lock);
-
-       tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
-                    (unsigned long)ps);
-       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
-                      qtnf_pcie_pearl_rx_poll, 10);
        INIT_WORK(&bus->fw_work, qtnf_pearl_fw_work_handler);
 
        ps->pcie_reg_base = ps->base.dmareg_bar;
        ps->bda = ps->base.epmem_bar;
        writel(ps->base.msi_enabled, &ps->bda->bda_rc_msi_enabled);
 
-       ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
-       ipc_int.arg = ps;
-       qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
-                              &ps->bda->bda_shm_reg2, &ipc_int);
-
-       ret = qtnf_pearl_check_chip_id(ps);
-       if (ret)
-               goto error;
-
-       ret = qtnf_pcie_pearl_init_xfer(ps);
+       ret = qtnf_pcie_pearl_init_xfer(ps, tx_bd_size);
        if (ret) {
                pr_err("PCIE xfer init failed\n");
-               goto error;
+               return ret;
        }
 
        /* init default irq settings */
@@ -1155,95 +1096,63 @@ static int qtnf_pcie_pearl_probe(struct pci_dev *pdev,
 
        ret = devm_request_irq(&pdev->dev, pdev->irq,
                               &qtnf_pcie_pearl_interrupt, 0,
-                              "qtnf_pcie_irq", (void *)bus);
+                              "qtnf_pearl_irq", (void *)bus);
        if (ret) {
                pr_err("failed to request pcie irq %d\n", pdev->irq);
-               goto err_xfer;
+               qtnf_pearl_free_xfer_buffers(ps);
+               return ret;
        }
 
-       qtnf_pcie_bringup_fw_async(bus);
-
-       return 0;
+       tasklet_init(&ps->base.reclaim_tq, qtnf_pearl_reclaim_tasklet_fn,
+                    (unsigned long)ps);
+       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+                      qtnf_pcie_pearl_rx_poll, 10);
 
-err_xfer:
-       qtnf_pearl_free_xfer_buffers(ps);
-error:
-       qtnf_pcie_remove(bus, &ps->base);
+       ipc_int.fn = qtnf_pcie_pearl_ipc_gen_ep_int;
+       ipc_int.arg = ps;
+       qtnf_pcie_init_shm_ipc(&ps->base, &ps->bda->bda_shm_reg1,
+                              &ps->bda->bda_shm_reg2, &ipc_int);
 
-       return ret;
+       return 0;
 }
 
-static void qtnf_pcie_pearl_remove(struct pci_dev *pdev)
+static void qtnf_pcie_pearl_remove(struct qtnf_bus *bus)
 {
-       struct qtnf_pcie_pearl_state *ps;
-       struct qtnf_bus *bus;
-
-       bus = pci_get_drvdata(pdev);
-       if (!bus)
-               return;
-
-       ps = get_bus_priv(bus);
+       struct qtnf_pcie_pearl_state *ps = get_bus_priv(bus);
 
-       qtnf_pcie_remove(bus, &ps->base);
        qtnf_pearl_reset_ep(ps);
        qtnf_pearl_free_xfer_buffers(ps);
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int qtnf_pcie_pearl_suspend(struct device *dev)
+static int qtnf_pcie_pearl_suspend(struct qtnf_bus *bus)
 {
        return -EOPNOTSUPP;
 }
 
-static int qtnf_pcie_pearl_resume(struct device *dev)
+static int qtnf_pcie_pearl_resume(struct qtnf_bus *bus)
 {
        return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
-
-#ifdef CONFIG_PM_SLEEP
-/* Power Management Hooks */
-static SIMPLE_DEV_PM_OPS(qtnf_pcie_pearl_pm_ops, qtnf_pcie_pearl_suspend,
-                        qtnf_pcie_pearl_resume);
 #endif
 
-static const struct pci_device_id qtnf_pcie_devid_table[] = {
-       {
-               PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-       },
-       { },
-};
+struct qtnf_bus *qtnf_pcie_pearl_alloc(struct pci_dev *pdev)
+{
+       struct qtnf_bus *bus;
+       struct qtnf_pcie_pearl_state *ps;
 
-MODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table);
+       bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ps), GFP_KERNEL);
+       if (!bus)
+               return NULL;
 
-static struct pci_driver qtnf_pcie_pearl_drv_data = {
-       .name = DRV_NAME,
-       .id_table = qtnf_pcie_devid_table,
-       .probe = qtnf_pcie_pearl_probe,
-       .remove = qtnf_pcie_pearl_remove,
+       ps = get_bus_priv(bus);
+       ps->base.probe_cb = qtnf_pcie_pearl_probe;
+       ps->base.remove_cb = qtnf_pcie_pearl_remove;
+       ps->base.dma_mask_get_cb = qtnf_pearl_dma_mask_get;
 #ifdef CONFIG_PM_SLEEP
-       .driver = {
-               .pm = &qtnf_pcie_pearl_pm_ops,
-       },
+       ps->base.resume_cb = qtnf_pcie_pearl_resume;
+       ps->base.suspend_cb = qtnf_pcie_pearl_suspend;
 #endif
-};
-
-static int __init qtnf_pcie_pearl_register(void)
-{
-       pr_info("register Quantenna QSR10g FullMAC PCIE driver\n");
-       return pci_register_driver(&qtnf_pcie_pearl_drv_data);
-}
 
-static void __exit qtnf_pcie_pearl_exit(void)
-{
-       pr_info("unregister Quantenna QSR10g FullMAC PCIE driver\n");
-       pci_unregister_driver(&qtnf_pcie_pearl_drv_data);
+       return bus;
 }
-
-module_init(qtnf_pcie_pearl_register);
-module_exit(qtnf_pcie_pearl_exit);
-
-MODULE_AUTHOR("Quantenna Communications");
-MODULE_DESCRIPTION("Quantenna QSR10g PCIe bus driver for 802.11 wireless LAN.");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
new file mode 100644 (file)
index 0000000..598edb8
--- /dev/null
@@ -0,0 +1,1219 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2018 Quantenna Communications */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/crc32.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/circ_buf.h>
+
+#include "pcie_priv.h"
+#include "topaz_pcie_regs.h"
+#include "topaz_pcie_ipc.h"
+#include "qtn_hw_ids.h"
+#include "core.h"
+#include "bus.h"
+#include "shm_ipc.h"
+#include "debug.h"
+
+#define TOPAZ_TX_BD_SIZE_DEFAULT       128
+
+struct qtnf_topaz_tx_bd {
+       __le32 addr;
+       __le32 info;
+} __packed;
+
+struct qtnf_topaz_rx_bd {
+       __le32 addr;
+       __le32 info;
+} __packed;
+
+struct qtnf_extra_bd_params {
+       __le32 param1;
+       __le32 param2;
+       __le32 param3;
+       __le32 param4;
+} __packed;
+
+#define QTNF_BD_PARAM_OFFSET(n)        offsetof(struct qtnf_extra_bd_params, param##n)
+
+struct vmac_pkt_info {
+       __le32 addr;
+       __le32 info;
+};
+
+struct qtnf_topaz_bda {
+       __le16  bda_len;
+       __le16  bda_version;
+       __le32  bda_bootstate;
+       __le32  bda_dma_mask;
+       __le32  bda_dma_offset;
+       __le32  bda_flags;
+       __le32  bda_img;
+       __le32  bda_img_size;
+       __le32  bda_ep2h_irqstatus;
+       __le32  bda_h2ep_irqstatus;
+       __le32  bda_msi_addr;
+       u8      reserved1[56];
+       __le32  bda_flashsz;
+       u8      bda_boardname[PCIE_BDA_NAMELEN];
+       __le32  bda_pci_pre_status;
+       __le32  bda_pci_endian;
+       __le32  bda_pci_post_status;
+       __le32  bda_h2ep_txd_budget;
+       __le32  bda_ep2h_txd_budget;
+       __le32  bda_rc_rx_bd_base;
+       __le32  bda_rc_rx_bd_num;
+       __le32  bda_rc_tx_bd_base;
+       __le32  bda_rc_tx_bd_num;
+       u8      bda_ep_link_state;
+       u8      bda_rc_link_state;
+       u8      bda_rc_msi_enabled;
+       u8      reserved2;
+       __le32  bda_ep_next_pkt;
+       struct vmac_pkt_info request[QTN_PCIE_RC_TX_QUEUE_LEN];
+       struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096);
+       struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096);
+} __packed;
+
+struct qtnf_pcie_topaz_state {
+       struct qtnf_pcie_bus_priv base;
+       struct qtnf_topaz_bda __iomem *bda;
+
+       dma_addr_t dma_msi_dummy;
+       u32 dma_msi_imwr;
+
+       struct qtnf_topaz_tx_bd *tx_bd_vbase;
+       struct qtnf_topaz_rx_bd *rx_bd_vbase;
+
+       __le32 __iomem *ep_next_rx_pkt;
+       __le32 __iomem *txqueue_wake;
+       __le32 __iomem *ep_pmstate;
+
+       unsigned long rx_pkt_count;
+};
+
+static void qtnf_deassert_intx(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+       u32 cfg;
+
+       cfg = readl(reg);
+       cfg &= ~TOPAZ_ASSERT_INTX;
+       qtnf_non_posted_write(cfg, reg);
+}
+
+static inline int qtnf_topaz_intx_asserted(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
+       u32 cfg = readl(reg);
+
+       return !!(cfg & TOPAZ_ASSERT_INTX);
+}
+
+static void qtnf_topaz_reset_ep(struct qtnf_pcie_topaz_state *ts)
+{
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RST_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+       msleep(QTN_EP_RESET_WAIT_MS);
+       pci_restore_state(ts->base.pdev);
+}
+
+static void setup_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       ts->dma_msi_imwr = readl(reg);
+}
+
+static void enable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       qtnf_non_posted_write(ts->dma_msi_imwr, reg);
+}
+
+static void disable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
+{
+       void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
+
+       qtnf_non_posted_write(QTN_HOST_LO32(ts->dma_msi_dummy), reg);
+}
+
+static void qtnf_topaz_ipc_gen_ep_int(void *arg)
+{
+       struct qtnf_pcie_topaz_state *ts = arg;
+
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_CTRL_IRQ),
+              TOPAZ_CTL_M2L_INT(ts->base.sysctl_bar));
+}
+
+static int qtnf_is_state(__le32 __iomem *reg, u32 state)
+{
+       u32 s = readl(reg);
+
+       return (s == state);
+}
+
+static void qtnf_set_state(__le32 __iomem *reg, u32 state)
+{
+       qtnf_non_posted_write(state, reg);
+}
+
+static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms)
+{
+       u32 timeout = 0;
+
+       while ((qtnf_is_state(reg, state) == 0)) {
+               usleep_range(1000, 1200);
+               if (++timeout > delay_in_ms)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int topaz_alloc_bd_table(struct qtnf_pcie_topaz_state *ts,
+                               struct qtnf_topaz_bda __iomem *bda)
+{
+       struct qtnf_extra_bd_params __iomem *extra_params;
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       dma_addr_t paddr;
+       void *vaddr;
+       int len;
+       int i;
+
+       /* bd table */
+
+       len = priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd) +
+               priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd) +
+                       sizeof(struct qtnf_extra_bd_params);
+
+       vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL);
+       if (!vaddr)
+               return -ENOMEM;
+
+       memset(vaddr, 0, len);
+
+       /* tx bd */
+
+       ts->tx_bd_vbase = vaddr;
+       qtnf_non_posted_write(paddr, &bda->bda_rc_tx_bd_base);
+
+       for (i = 0; i < priv->tx_bd_num; i++)
+               ts->tx_bd_vbase[i].info |= cpu_to_le32(QTN_BD_EMPTY);
+
+       pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+       priv->tx_bd_r_index = 0;
+       priv->tx_bd_w_index = 0;
+
+       /* rx bd */
+
+       vaddr = ((struct qtnf_topaz_tx_bd *)vaddr) + priv->tx_bd_num;
+       paddr += priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd);
+
+       ts->rx_bd_vbase = vaddr;
+       qtnf_non_posted_write(paddr, &bda->bda_rc_rx_bd_base);
+
+       pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
+
+       /* extra shared params */
+
+       vaddr = ((struct qtnf_topaz_rx_bd *)vaddr) + priv->rx_bd_num;
+       paddr += priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd);
+
+       extra_params = (struct qtnf_extra_bd_params __iomem *)vaddr;
+
+       ts->ep_next_rx_pkt = &extra_params->param1;
+       qtnf_non_posted_write(paddr + QTNF_BD_PARAM_OFFSET(1),
+                             &bda->bda_ep_next_pkt);
+       ts->txqueue_wake = &extra_params->param2;
+       ts->ep_pmstate = &extra_params->param3;
+       ts->dma_msi_dummy = paddr + QTNF_BD_PARAM_OFFSET(4);
+
+       return 0;
+}
+
+static int
+topaz_skb2rbd_attach(struct qtnf_pcie_topaz_state *ts, u16 index, u32 wrap)
+{
+       struct qtnf_topaz_rx_bd *rxbd = &ts->rx_bd_vbase[index];
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+
+       skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC);
+       if (!skb) {
+               ts->base.rx_skb[index] = NULL;
+               return -ENOMEM;
+       }
+
+       ts->base.rx_skb[index] = skb;
+
+       paddr = pci_map_single(ts->base.pdev, skb->data,
+                              SKB_BUF_SIZE, PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(ts->base.pdev, paddr)) {
+               pr_err("skb mapping error: %pad\n", &paddr);
+               return -ENOMEM;
+       }
+
+       rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr));
+       rxbd->info = cpu_to_le32(QTN_BD_EMPTY | wrap);
+
+       ts->base.rx_bd_w_index = index;
+
+       return 0;
+}
+
+static int topaz_alloc_rx_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+       u16 i;
+       int ret = 0;
+
+       memset(ts->rx_bd_vbase, 0x0,
+              ts->base.rx_bd_num * sizeof(struct qtnf_topaz_rx_bd));
+
+       for (i = 0; i < ts->base.rx_bd_num; i++) {
+               ret = topaz_skb2rbd_attach(ts, i, 0);
+               if (ret)
+                       break;
+       }
+
+       ts->rx_bd_vbase[ts->base.rx_bd_num - 1].info |=
+                                               cpu_to_le32(QTN_BD_WRAP);
+
+       return ret;
+}
+
+/* all rx/tx activity should have ceased before calling this function */
+static void qtnf_topaz_free_xfer_buffers(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_rx_bd *rxbd;
+       struct qtnf_topaz_tx_bd *txbd;
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int i;
+
+       /* free rx buffers */
+       for (i = 0; i < priv->rx_bd_num; i++) {
+               if (priv->rx_skb && priv->rx_skb[i]) {
+                       rxbd = &ts->rx_bd_vbase[i];
+                       skb = priv->rx_skb[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb_any(skb);
+                       priv->rx_skb[i] = NULL;
+                       rxbd->addr = 0;
+                       rxbd->info = 0;
+               }
+       }
+
+       /* free tx buffers */
+       for (i = 0; i < priv->tx_bd_num; i++) {
+               if (priv->tx_skb && priv->tx_skb[i]) {
+                       txbd = &ts->tx_bd_vbase[i];
+                       skb = priv->tx_skb[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_TODEVICE);
+                       dev_kfree_skb_any(skb);
+                       priv->tx_skb[i] = NULL;
+                       txbd->addr = 0;
+                       txbd->info = 0;
+               }
+       }
+}
+
+static int qtnf_pcie_topaz_init_xfer(struct qtnf_pcie_topaz_state *ts,
+                                    unsigned int tx_bd_size)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       int ret;
+
+       if (tx_bd_size == 0)
+               tx_bd_size = TOPAZ_TX_BD_SIZE_DEFAULT;
+
+       /* check TX BD queue max length according to struct qtnf_topaz_bda */
+       if (tx_bd_size > QTN_PCIE_RC_TX_QUEUE_LEN) {
+               pr_warn("TX BD queue cannot exceed %d\n",
+                       QTN_PCIE_RC_TX_QUEUE_LEN);
+               tx_bd_size = QTN_PCIE_RC_TX_QUEUE_LEN;
+       }
+
+       priv->tx_bd_num = tx_bd_size;
+       qtnf_non_posted_write(priv->tx_bd_num, &bda->bda_rc_tx_bd_num);
+       qtnf_non_posted_write(priv->rx_bd_num, &bda->bda_rc_rx_bd_num);
+
+       priv->rx_bd_w_index = 0;
+       priv->rx_bd_r_index = 0;
+
+       ret = qtnf_pcie_alloc_skb_array(priv);
+       if (ret) {
+               pr_err("failed to allocate skb array\n");
+               return ret;
+       }
+
+       ret = topaz_alloc_bd_table(ts, bda);
+       if (ret) {
+               pr_err("failed to allocate bd table\n");
+               return ret;
+       }
+
+       ret = topaz_alloc_rx_buffers(ts);
+       if (ret) {
+               pr_err("failed to allocate rx buffers\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static void qtnf_topaz_data_tx_reclaim(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_tx_bd *txbd;
+       struct sk_buff *skb;
+       unsigned long flags;
+       dma_addr_t paddr;
+       u32 tx_done_index;
+       int count = 0;
+       int i;
+
+       spin_lock_irqsave(&priv->tx_reclaim_lock, flags);
+
+       tx_done_index = readl(ts->ep_next_rx_pkt);
+       i = priv->tx_bd_r_index;
+
+       if (CIRC_CNT(priv->tx_bd_w_index, tx_done_index, priv->tx_bd_num))
+               writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+                      TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+       while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) {
+               skb = priv->tx_skb[i];
+
+               if (likely(skb)) {
+                       txbd = &ts->tx_bd_vbase[i];
+                       paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, skb->len,
+                                        PCI_DMA_TODEVICE);
+
+                       if (skb->dev) {
+                               qtnf_update_tx_stats(skb->dev, skb);
+                               if (unlikely(priv->tx_stopped)) {
+                                       qtnf_wake_all_queues(skb->dev);
+                                       priv->tx_stopped = 0;
+                               }
+                       }
+
+                       dev_kfree_skb_any(skb);
+               }
+
+               priv->tx_skb[i] = NULL;
+               count++;
+
+               if (++i >= priv->tx_bd_num)
+                       i = 0;
+       }
+
+       priv->tx_reclaim_done += count;
+       priv->tx_reclaim_req++;
+       priv->tx_bd_r_index = i;
+
+       spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags);
+}
+
+static void qtnf_try_stop_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+
+       if (ndev) {
+               netif_tx_stop_all_queues(ndev);
+               ts->base.tx_stopped = 1;
+       }
+
+       writel(0x0, ts->txqueue_wake);
+
+       /* sync up tx queue status before generating interrupt */
+       dma_wmb();
+
+       /* send irq to card: tx stopped */
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       /* schedule reclaim attempt */
+       tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_try_wake_xmit(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       int ready;
+
+       ready = readl(ts->txqueue_wake);
+       if (ready) {
+               netif_wake_queue(ndev);
+       } else {
+               /* re-send irq to card: tx stopped */
+               writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
+                      TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+       }
+}
+
+static int qtnf_tx_queue_ready(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+       if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                       priv->tx_bd_num)) {
+               qtnf_topaz_data_tx_reclaim(ts);
+
+               if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                               priv->tx_bd_num)) {
+                       priv->tx_full_count++;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct qtnf_topaz_tx_bd *txbd;
+       dma_addr_t skb_paddr;
+       unsigned long flags;
+       int ret = 0;
+       int len;
+       int i;
+
+       spin_lock_irqsave(&priv->tx_lock, flags);
+
+       if (!qtnf_tx_queue_ready(ts)) {
+               qtnf_try_stop_xmit(bus, skb->dev);
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
+               return NETDEV_TX_BUSY;
+       }
+
+       i = priv->tx_bd_w_index;
+       priv->tx_skb[i] = skb;
+       len = skb->len;
+
+       skb_paddr = pci_map_single(priv->pdev, skb->data,
+                                  skb->len, PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(priv->pdev, skb_paddr)) {
+               ret = -ENOMEM;
+               goto tx_done;
+       }
+
+       txbd = &ts->tx_bd_vbase[i];
+       txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr));
+
+       writel(QTN_HOST_LO32(skb_paddr), &bda->request[i].addr);
+       writel(len | QTN_PCIE_TX_VALID_PKT, &bda->request[i].info);
+
+       /* sync up descriptor updates before generating interrupt */
+       dma_wmb();
+
+       /* generate irq to card: tx done */
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
+              TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+       if (++i >= priv->tx_bd_num)
+               i = 0;
+
+       priv->tx_bd_w_index = i;
+
+tx_done:
+       if (ret) {
+               if (skb->dev)
+                       skb->dev->stats.tx_dropped++;
+               dev_kfree_skb_any(skb);
+       }
+
+       priv->tx_done_count++;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+       qtnf_topaz_data_tx_reclaim(ts);
+
+       return NETDEV_TX_OK;
+}
+
+static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
+{
+       struct qtnf_bus *bus = (struct qtnf_bus *)data;
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+
+       if (!priv->msi_enabled && !qtnf_topaz_intx_asserted(ts))
+               return IRQ_NONE;
+
+       priv->pcie_irq_count++;
+
+       qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in);
+       qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out);
+
+       if (napi_schedule_prep(&bus->mux_napi)) {
+               disable_rx_irqs(ts);
+               __napi_schedule(&bus->mux_napi);
+       }
+
+       tasklet_hi_schedule(&priv->reclaim_tq);
+
+       if (!priv->msi_enabled)
+               qtnf_deassert_intx(ts);
+
+       return IRQ_HANDLED;
+}
+
+static int qtnf_rx_data_ready(struct qtnf_pcie_topaz_state *ts)
+{
+       u16 index = ts->base.rx_bd_r_index;
+       struct qtnf_topaz_rx_bd *rxbd;
+       u32 descw;
+
+       rxbd = &ts->rx_bd_vbase[index];
+       descw = le32_to_cpu(rxbd->info);
+
+       if (descw & QTN_BD_EMPTY)
+               return 0;
+
+       return 1;
+}
+
+static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
+{
+       struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi);
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       struct net_device *ndev = NULL;
+       struct sk_buff *skb = NULL;
+       int processed = 0;
+       struct qtnf_topaz_rx_bd *rxbd;
+       dma_addr_t skb_paddr;
+       int consume;
+       u32 descw;
+       u32 poffset;
+       u32 psize;
+       u16 r_idx;
+       u16 w_idx;
+       int ret;
+
+       while (processed < budget) {
+               if (!qtnf_rx_data_ready(ts))
+                       goto rx_out;
+
+               r_idx = priv->rx_bd_r_index;
+               rxbd = &ts->rx_bd_vbase[r_idx];
+               descw = le32_to_cpu(rxbd->info);
+
+               skb = priv->rx_skb[r_idx];
+               poffset = QTN_GET_OFFSET(descw);
+               psize = QTN_GET_LEN(descw);
+               consume = 1;
+
+               if (descw & QTN_BD_EMPTY) {
+                       pr_warn("skip invalid rxbd[%d]\n", r_idx);
+                       consume = 0;
+               }
+
+               if (!skb) {
+                       pr_warn("skip missing rx_skb[%d]\n", r_idx);
+                       consume = 0;
+               }
+
+               if (skb && (skb_tailroom(skb) <  psize)) {
+                       pr_err("skip packet with invalid length: %u > %u\n",
+                              psize, skb_tailroom(skb));
+                       consume = 0;
+               }
+
+               if (skb) {
+                       skb_paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
+                       pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE,
+                                        PCI_DMA_FROMDEVICE);
+               }
+
+               if (consume) {
+                       skb_reserve(skb, poffset);
+                       skb_put(skb, psize);
+                       ndev = qtnf_classify_skb(bus, skb);
+                       if (likely(ndev)) {
+                               qtnf_update_rx_stats(ndev, skb);
+                               skb->protocol = eth_type_trans(skb, ndev);
+                               netif_receive_skb(skb);
+                       } else {
+                               pr_debug("drop untagged skb\n");
+                               bus->mux_dev.stats.rx_dropped++;
+                               dev_kfree_skb_any(skb);
+                       }
+               } else {
+                       if (skb) {
+                               bus->mux_dev.stats.rx_dropped++;
+                               dev_kfree_skb_any(skb);
+                       }
+               }
+
+               /* notify card about recv packets once per several packets */
+               if (((++ts->rx_pkt_count) & RX_DONE_INTR_MSK) == 0)
+                       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RX_DONE_IRQ),
+                              TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
+
+               priv->rx_skb[r_idx] = NULL;
+               if (++r_idx >= priv->rx_bd_num)
+                       r_idx = 0;
+
+               priv->rx_bd_r_index = r_idx;
+
+               /* repalce processed buffer by a new one */
+               w_idx = priv->rx_bd_w_index;
+               while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+                                 priv->rx_bd_num) > 0) {
+                       if (++w_idx >= priv->rx_bd_num)
+                               w_idx = 0;
+
+                       ret = topaz_skb2rbd_attach(ts, w_idx,
+                                                  descw & QTN_BD_WRAP);
+                       if (ret) {
+                               pr_err("failed to allocate new rx_skb[%d]\n",
+                                      w_idx);
+                               break;
+                       }
+               }
+
+               processed++;
+       }
+
+rx_out:
+       if (processed < budget) {
+               napi_complete(napi);
+               enable_rx_irqs(ts);
+       }
+
+       return processed;
+}
+
+static void
+qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       qtnf_try_wake_xmit(bus, ndev);
+       tasklet_hi_schedule(&ts->base.reclaim_tq);
+}
+
+static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       napi_enable(&bus->mux_napi);
+       enable_rx_irqs(ts);
+}
+
+static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       disable_rx_irqs(ts);
+       napi_disable(&bus->mux_napi);
+}
+
+static const struct qtnf_bus_ops qtnf_pcie_topaz_bus_ops = {
+       /* control path methods */
+       .control_tx     = qtnf_pcie_control_tx,
+
+       /* data path methods */
+       .data_tx                = qtnf_pcie_data_tx,
+       .data_tx_timeout        = qtnf_pcie_data_tx_timeout,
+       .data_rx_start          = qtnf_pcie_data_rx_start,
+       .data_rx_stop           = qtnf_pcie_data_rx_stop,
+};
+
+static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
+{
+       struct qtnf_bus *bus = dev_get_drvdata(s->private);
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       seq_printf(s, "pcie_irq_count(%u)\n", ts->base.pcie_irq_count);
+
+       return 0;
+}
+
+static int qtnf_dbg_pkt_stats(struct seq_file *s, void *data)
+{
+       struct qtnf_bus *bus = dev_get_drvdata(s->private);
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct qtnf_pcie_bus_priv *priv = &ts->base;
+       u32 tx_done_index = readl(ts->ep_next_rx_pkt);
+
+       seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
+       seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
+       seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
+       seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
+
+       seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
+       seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
+       seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
+
+       seq_printf(s, "tx host queue len(%u)\n",
+                  CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
+                           priv->tx_bd_num));
+       seq_printf(s, "tx reclaim queue len(%u)\n",
+                  CIRC_CNT(tx_done_index, priv->tx_bd_r_index,
+                           priv->tx_bd_num));
+       seq_printf(s, "tx card queue len(%u)\n",
+                  CIRC_CNT(priv->tx_bd_w_index, tx_done_index,
+                           priv->tx_bd_num));
+
+       seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
+       seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
+       seq_printf(s, "rx alloc queue len(%u)\n",
+                  CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
+                             priv->rx_bd_num));
+
+       return 0;
+}
+
+static void qtnf_reset_dma_offset(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 offset = readl(&bda->bda_dma_offset);
+
+       if ((offset & PCIE_DMA_OFFSET_ERROR_MASK) != PCIE_DMA_OFFSET_ERROR)
+               return;
+
+       writel(0x0, &bda->bda_dma_offset);
+}
+
+static int qtnf_pcie_endian_detect(struct qtnf_pcie_topaz_state *ts)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 timeout = 0;
+       u32 endian;
+       int ret = 0;
+
+       writel(QTN_PCI_ENDIAN_DETECT_DATA, &bda->bda_pci_endian);
+
+       /* flush endian modifications before status update */
+       dma_wmb();
+
+       writel(QTN_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_pre_status);
+
+       while (readl(&bda->bda_pci_post_status) !=
+              QTN_PCI_ENDIAN_VALID_STATUS) {
+               usleep_range(1000, 1200);
+               if (++timeout > QTN_FW_DL_TIMEOUT_MS) {
+                       pr_err("card endianness detection timed out\n");
+                       ret = -ETIMEDOUT;
+                       goto endian_out;
+               }
+       }
+
+       /* do not read before status is updated */
+       dma_rmb();
+
+       endian = readl(&bda->bda_pci_endian);
+       WARN(endian != QTN_PCI_LITTLE_ENDIAN,
+            "%s: unexpected card endianness", __func__);
+
+endian_out:
+       writel(0, &bda->bda_pci_pre_status);
+       writel(0, &bda->bda_pci_post_status);
+       writel(0, &bda->bda_pci_endian);
+
+       return ret;
+}
+
+static int qtnf_pre_init_ep(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       u32 flags;
+       int ret;
+
+       ret = qtnf_pcie_endian_detect(ts);
+       if (ret < 0) {
+               pr_err("failed to detect card endianness\n");
+               return ret;
+       }
+
+       writeb(ts->base.msi_enabled, &ts->bda->bda_rc_msi_enabled);
+       qtnf_reset_dma_offset(ts);
+
+       /* notify card about driver type and boot mode */
+       flags = readl(&bda->bda_flags) | QTN_BDA_HOST_QLINK_DRV;
+
+       if (ts->base.flashboot)
+               flags |= QTN_BDA_FLASH_BOOT;
+       else
+               flags &= ~QTN_BDA_FLASH_BOOT;
+
+       writel(flags, &bda->bda_flags);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_RDY);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("card is not ready to boot...\n");
+               return -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static int qtnf_post_init_ep(struct qtnf_pcie_topaz_state *ts)
+{
+       struct pci_dev *pdev = ts->base.pdev;
+
+       setup_rx_irqs(ts);
+       disable_rx_irqs(ts);
+
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_QLINK_DONE,
+                           QTN_FW_QLINK_TIMEOUT_MS))
+               return -ETIMEDOUT;
+
+       enable_irq(pdev->irq);
+       return 0;
+}
+
+static int
+qtnf_ep_fw_load(struct qtnf_pcie_topaz_state *ts, const u8 *fw, u32 fw_size)
+{
+       struct qtnf_topaz_bda __iomem *bda = ts->bda;
+       struct pci_dev *pdev = ts->base.pdev;
+       u32 remaining = fw_size;
+       u8 *curr = (u8 *)fw;
+       u32 blksize;
+       u32 nblocks;
+       u32 offset;
+       u32 count;
+       u32 size;
+       dma_addr_t paddr;
+       void *data;
+       int ret = 0;
+
+       pr_debug("FW upload started: fw_addr = 0x%p, size=%d\n", fw, fw_size);
+
+       blksize = ts->base.fw_blksize;
+
+       if (blksize < PAGE_SIZE)
+               blksize = PAGE_SIZE;
+
+       while (blksize >= PAGE_SIZE) {
+               pr_debug("allocating %u bytes to upload FW\n", blksize);
+               data = dma_alloc_coherent(&pdev->dev, blksize,
+                                         &paddr, GFP_KERNEL);
+               if (data)
+                       break;
+               blksize /= 2;
+       }
+
+       if (!data) {
+               pr_err("failed to allocate DMA buffer for FW upload\n");
+               ret = -ENOMEM;
+               goto fw_load_out;
+       }
+
+       nblocks = NBLOCKS(fw_size, blksize);
+       offset = readl(&bda->bda_dma_offset);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_LOAD);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_EP_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("card is not ready to download FW\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       for (count = 0 ; count < nblocks; count++) {
+               size = (remaining > blksize) ? blksize : remaining;
+
+               memcpy(data, curr, size);
+               qtnf_non_posted_write(paddr + offset, &bda->bda_img);
+               qtnf_non_posted_write(size, &bda->bda_img_size);
+
+               pr_debug("chunk[%u] VA[0x%p] PA[%pad] sz[%u]\n",
+                        count, (void *)curr, &paddr, size);
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+               if (qtnf_poll_state(&ts->bda->bda_bootstate,
+                                   QTN_BDA_FW_BLOCK_DONE,
+                                   QTN_FW_DL_TIMEOUT_MS)) {
+                       pr_err("confirmation for block #%d timed out\n", count);
+                       ret = -ETIMEDOUT;
+                       goto fw_load_map;
+               }
+
+               remaining = (remaining < size) ? remaining : (remaining - size);
+               curr += size;
+       }
+
+       /* upload completion mark: zero-sized block */
+       qtnf_non_posted_write(0, &bda->bda_img);
+       qtnf_non_posted_write(0, &bda->bda_img_size);
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_DONE,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("confirmation for the last block timed out\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       /* RC is done */
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_END);
+       if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_LOAD_DONE,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("confirmation for FW upload completion timed out\n");
+               ret = -ETIMEDOUT;
+               goto fw_load_map;
+       }
+
+       pr_debug("FW upload completed: totally sent %d blocks\n", count);
+
+fw_load_map:
+       dma_free_coherent(&pdev->dev, blksize, data, paddr);
+
+fw_load_out:
+       return ret;
+}
+
+static int qtnf_topaz_fw_upload(struct qtnf_pcie_topaz_state *ts,
+                               const char *fwname)
+{
+       const struct firmware *fw;
+       struct pci_dev *pdev = ts->base.pdev;
+       int ret;
+
+       if (qtnf_poll_state(&ts->bda->bda_bootstate,
+                           QTN_BDA_FW_LOAD_RDY,
+                           QTN_FW_DL_TIMEOUT_MS)) {
+               pr_err("%s: card is not ready\n", fwname);
+               return -1;
+       }
+
+       pr_info("starting firmware upload: %s\n", fwname);
+
+       ret = request_firmware(&fw, fwname, &pdev->dev);
+       if (ret < 0) {
+               pr_err("%s: request_firmware error %d\n", fwname, ret);
+               return -1;
+       }
+
+       ret = qtnf_ep_fw_load(ts, fw->data, fw->size);
+       release_firmware(fw);
+
+       if (ret)
+               pr_err("%s: FW upload error\n", fwname);
+
+       return ret;
+}
+
+static void qtnf_topaz_fw_work_handler(struct work_struct *work)
+{
+       struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work);
+       struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
+       int ret;
+       int bootloader_needed = readl(&ts->bda->bda_flags) & QTN_BDA_XMIT_UBOOT;
+
+       qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_BOOT);
+
+       if (bootloader_needed) {
+               ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_BOOTLD_NAME);
+               if (ret)
+                       goto fw_load_exit;
+
+               ret = qtnf_pre_init_ep(bus);
+               if (ret)
+                       goto fw_load_exit;
+
+               qtnf_set_state(&ts->bda->bda_bootstate,
+                              QTN_BDA_FW_TARGET_BOOT);
+       }
+
+       if (ts->base.flashboot) {
+               pr_info("booting firmware from flash\n");
+
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_FLASH_BOOT,
+                                     QTN_FW_DL_TIMEOUT_MS);
+               if (ret)
+                       goto fw_load_exit;
+       } else {
+               ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_FW_NAME);
+               if (ret)
+                       goto fw_load_exit;
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_START);
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_CONFIG,
+                                     QTN_FW_QLINK_TIMEOUT_MS);
+               if (ret) {
+                       pr_err("FW bringup timed out\n");
+                       goto fw_load_exit;
+               }
+
+               qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_RUN);
+               ret = qtnf_poll_state(&ts->bda->bda_bootstate,
+                                     QTN_BDA_FW_RUNNING,
+                                     QTN_FW_QLINK_TIMEOUT_MS);
+               if (ret) {
+                       pr_err("card bringup timed out\n");
+                       goto fw_load_exit;
+               }
+       }
+
+       pr_info("firmware is up and running\n");
+
+       ret = qtnf_post_init_ep(ts);
+       if (ret)
+               pr_err("FW runtime failure\n");
+
+fw_load_exit:
+       qtnf_pcie_fw_boot_done(bus, ret ? false : true);
+
+       if (ret == 0) {
+               qtnf_debugfs_add_entry(bus, "pkt_stats", qtnf_dbg_pkt_stats);
+               qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
+       }
+}
+
+static void qtnf_reclaim_tasklet_fn(unsigned long data)
+{
+       struct qtnf_pcie_topaz_state *ts = (void *)data;
+
+       qtnf_topaz_data_tx_reclaim(ts);
+}
+
+static u64 qtnf_topaz_dma_mask_get(void)
+{
+       return DMA_BIT_MASK(32);
+}
+
+static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, unsigned int tx_bd_num)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+       struct qtnf_shm_ipc_int ipc_int;
+       unsigned long irqflags;
+       int ret;
+
+       bus->bus_ops = &qtnf_pcie_topaz_bus_ops;
+       INIT_WORK(&bus->fw_work, qtnf_topaz_fw_work_handler);
+       ts->bda = ts->base.epmem_bar;
+
+       /* assign host msi irq before card init */
+       if (ts->base.msi_enabled)
+               irqflags = IRQF_NOBALANCING;
+       else
+               irqflags = IRQF_NOBALANCING | IRQF_SHARED;
+
+       ret = devm_request_irq(&pdev->dev, pdev->irq,
+                              &qtnf_pcie_topaz_interrupt,
+                              irqflags, "qtnf_topaz_irq", (void *)bus);
+       if (ret) {
+               pr_err("failed to request pcie irq %d\n", pdev->irq);
+               return ret;
+       }
+
+       disable_irq(pdev->irq);
+
+       ret = qtnf_pre_init_ep(bus);
+       if (ret) {
+               pr_err("failed to init card\n");
+               return ret;
+       }
+
+       ret = qtnf_pcie_topaz_init_xfer(ts, tx_bd_num);
+       if (ret) {
+               pr_err("PCIE xfer init failed\n");
+               return ret;
+       }
+
+       tasklet_init(&ts->base.reclaim_tq, qtnf_reclaim_tasklet_fn,
+                    (unsigned long)ts);
+       netif_napi_add(&bus->mux_dev, &bus->mux_napi,
+                      qtnf_topaz_rx_poll, 10);
+
+       ipc_int.fn = qtnf_topaz_ipc_gen_ep_int;
+       ipc_int.arg = ts;
+       qtnf_pcie_init_shm_ipc(&ts->base, &ts->bda->bda_shm_reg1,
+                              &ts->bda->bda_shm_reg2, &ipc_int);
+
+       return 0;
+}
+
+static void qtnf_pcie_topaz_remove(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+
+       qtnf_topaz_reset_ep(ts);
+       qtnf_topaz_free_xfer_buffers(ts);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qtnf_pcie_topaz_suspend(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+
+       writel((u32 __force)PCI_D3hot, ts->ep_pmstate);
+       dma_wmb();
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, PCI_D3hot, 1);
+       pci_set_power_state(pdev, PCI_D3hot);
+
+       return 0;
+}
+
+static int qtnf_pcie_topaz_resume(struct qtnf_bus *bus)
+{
+       struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
+       struct pci_dev *pdev = ts->base.pdev;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       pci_enable_wake(pdev, PCI_D0, 0);
+
+       writel((u32 __force)PCI_D0, ts->ep_pmstate);
+       dma_wmb();
+       writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
+              TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
+
+       return 0;
+}
+#endif
+
+struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev)
+{
+       struct qtnf_bus *bus;
+       struct qtnf_pcie_topaz_state *ts;
+
+       bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ts), GFP_KERNEL);
+       if (!bus)
+               return NULL;
+
+       ts = get_bus_priv(bus);
+       ts->base.probe_cb = qtnf_pcie_topaz_probe;
+       ts->base.remove_cb = qtnf_pcie_topaz_remove;
+       ts->base.dma_mask_get_cb = qtnf_topaz_dma_mask_get;
+#ifdef CONFIG_PM_SLEEP
+       ts->base.resume_cb = qtnf_pcie_topaz_resume;
+       ts->base.suspend_cb = qtnf_pcie_topaz_suspend;
+#endif
+
+       return bus;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_ipc.h
new file mode 100644 (file)
index 0000000..eb30e9d
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef _QTN_FMAC_PCIE_IPC_H_
+#define _QTN_FMAC_PCIE_IPC_H_
+
+#include <linux/types.h>
+
+#include "shm_ipc_defs.h"
+
+/* EP/RC status and flags */
+#define QTN_BDA_PCIE_INIT              0x01
+#define QTN_BDA_PCIE_RDY               0x02
+#define QTN_BDA_FW_LOAD_RDY            0x03
+#define QTN_BDA_FW_LOAD_DONE           0x04
+#define QTN_BDA_FW_START               0x05
+#define QTN_BDA_FW_RUN                 0x06
+#define QTN_BDA_FW_HOST_RDY            0x07
+#define QTN_BDA_FW_TARGET_RDY          0x11
+#define QTN_BDA_FW_TARGET_BOOT         0x12
+#define QTN_BDA_FW_FLASH_BOOT          0x13
+#define QTN_BDA_FW_QLINK_DONE          0x14
+#define QTN_BDA_FW_HOST_LOAD           0x08
+#define QTN_BDA_FW_BLOCK_DONE          0x09
+#define QTN_BDA_FW_BLOCK_RDY           0x0A
+#define QTN_BDA_FW_EP_RDY              0x0B
+#define QTN_BDA_FW_BLOCK_END           0x0C
+#define QTN_BDA_FW_CONFIG              0x0D
+#define QTN_BDA_FW_RUNNING             0x0E
+#define QTN_BDA_PCIE_FAIL              0x82
+#define QTN_BDA_FW_LOAD_FAIL           0x85
+
+#define QTN_BDA_RCMODE                 BIT(1)
+#define QTN_BDA_MSI                    BIT(2)
+#define QTN_BDA_HOST_CALCMD            BIT(3)
+#define QTN_BDA_FLASH_PRESENT          BIT(4)
+#define QTN_BDA_FLASH_BOOT             BIT(5)
+#define QTN_BDA_XMIT_UBOOT             BIT(6)
+#define QTN_BDA_HOST_QLINK_DRV         BIT(7)
+#define QTN_BDA_TARGET_FBOOT_ERR       BIT(8)
+#define QTN_BDA_TARGET_FWLOAD_ERR      BIT(9)
+#define QTN_BDA_HOST_NOFW_ERR          BIT(12)
+#define QTN_BDA_HOST_MEMALLOC_ERR      BIT(13)
+#define QTN_BDA_HOST_MEMMAP_ERR                BIT(14)
+#define QTN_BDA_VER(x)                 (((x) >> 4) & 0xFF)
+#define QTN_BDA_ERROR_MASK             0xFF00
+
+/* registers and shmem address macros */
+#if BITS_PER_LONG == 64
+#define QTN_HOST_HI32(a)       ((u32)(((u64)a) >> 32))
+#define QTN_HOST_LO32(a)       ((u32)(((u64)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l)    ((((u64)h) << 32) | ((u64)l))
+#elif BITS_PER_LONG == 32
+#define QTN_HOST_HI32(a)       0
+#define QTN_HOST_LO32(a)       ((u32)(((u32)a) & 0xffffffffUL))
+#define QTN_HOST_ADDR(h, l)    ((u32)l)
+#else
+#error Unexpected BITS_PER_LONG value
+#endif
+
+#define QTN_PCIE_BDA_VERSION           0x1001
+
+#define PCIE_BDA_NAMELEN               32
+
+#define QTN_PCIE_RC_TX_QUEUE_LEN       256
+#define QTN_PCIE_TX_VALID_PKT          0x80000000
+#define QTN_PCIE_PKT_LEN_MASK          0xffff
+
+#define QTN_BD_EMPTY           ((uint32_t)0x00000001)
+#define QTN_BD_WRAP            ((uint32_t)0x00000002)
+#define QTN_BD_MASK_LEN                ((uint32_t)0xFFFF0000)
+#define QTN_BD_MASK_OFFSET     ((uint32_t)0x0000FF00)
+
+#define QTN_GET_LEN(x)         (((x) >> 16) & 0xFFFF)
+#define QTN_GET_OFFSET(x)      (((x) >> 8) & 0xFF)
+#define QTN_SET_LEN(len)       (((len) & 0xFFFF) << 16)
+#define QTN_SET_OFFSET(of)     (((of) & 0xFF) << 8)
+
+#define RX_DONE_INTR_MSK       ((0x1 << 6) - 1)
+
+#define PCIE_DMA_OFFSET_ERROR          0xFFFF
+#define PCIE_DMA_OFFSET_ERROR_MASK     0xFFFF
+
+#define QTN_PCI_ENDIAN_DETECT_DATA     0x12345678
+#define QTN_PCI_ENDIAN_REVERSE_DATA    0x78563412
+#define QTN_PCI_ENDIAN_VALID_STATUS    0x3c3c3c3c
+#define QTN_PCI_ENDIAN_INVALID_STATUS  0
+#define QTN_PCI_LITTLE_ENDIAN          0
+#define QTN_PCI_BIG_ENDIAN             0xffffffff
+
+#define NBLOCKS(size, blksize)         \
+       ((size) / (blksize) + (((size) % (blksize) > 0) ? 1 : 0))
+
+#endif /* _QTN_FMAC_PCIE_IPC_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie_regs.h
new file mode 100644 (file)
index 0000000..4782e1e
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018 Quantenna Communications */
+
+#ifndef __TOPAZ_PCIE_H
+#define __TOPAZ_PCIE_H
+
+/* Topaz PCIe DMA registers */
+#define PCIE_DMA_WR_INTR_STATUS(base)          ((base) + 0x9bc)
+#define PCIE_DMA_WR_INTR_MASK(base)            ((base) + 0x9c4)
+#define PCIE_DMA_WR_INTR_CLR(base)             ((base) + 0x9c8)
+#define PCIE_DMA_WR_ERR_STATUS(base)           ((base) + 0x9cc)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(base)   ((base) + 0x9D0)
+#define PCIE_DMA_WR_DONE_IMWR_ADDR_HIGH(base)  ((base) + 0x9d4)
+
+#define PCIE_DMA_RD_INTR_STATUS(base)          ((base) + 0x310)
+#define PCIE_DMA_RD_INTR_MASK(base)            ((base) + 0x319)
+#define PCIE_DMA_RD_INTR_CLR(base)             ((base) + 0x31c)
+#define PCIE_DMA_RD_ERR_STATUS_LOW(base)       ((base) + 0x324)
+#define PCIE_DMA_RD_ERR_STATUS_HIGH(base)      ((base) + 0x328)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_LOW(base)   ((base) + 0x33c)
+#define PCIE_DMA_RD_DONE_IMWR_ADDR_HIGH(base)  ((base) + 0x340)
+
+/* Topaz LHost IPC4 interrupt */
+#define TOPAZ_LH_IPC4_INT(base)                        ((base) + 0x13C)
+#define TOPAZ_LH_IPC4_INT_MASK(base)           ((base) + 0x140)
+
+#define TOPAZ_RC_TX_DONE_IRQ                   (0)
+#define TOPAZ_RC_RST_EP_IRQ                    (1)
+#define TOPAZ_RC_TX_STOP_IRQ                   (2)
+#define TOPAZ_RC_RX_DONE_IRQ                   (3)
+#define TOPAZ_RC_PM_EP_IRQ                     (4)
+
+/* Topaz LHost M2L interrupt */
+#define TOPAZ_CTL_M2L_INT(base)                        ((base) + 0x2C)
+#define TOPAZ_CTL_M2L_INT_MASK(base)           ((base) + 0x30)
+
+#define TOPAZ_RC_CTRL_IRQ                      (6)
+
+#define TOPAZ_IPC_IRQ_WORD(irq)                        (BIT(irq) | BIT(irq + 16))
+
+/* PCIe legacy INTx */
+#define TOPAZ_PCIE_CFG0_OFFSET (0x6C)
+#define TOPAZ_ASSERT_INTX      BIT(9)
+
+#endif /* __TOPAZ_PCIE_H */
index 1fe798a..40295a5 100644 (file)
@@ -23,7 +23,7 @@
 
 /* PCIE Device IDs */
 
-#define        PCIE_DEVICE_ID_QTN_PEARL        (0x0008)
+#define        PCIE_DEVICE_ID_QSR              (0x0008)
 
 #define QTN_REG_SYS_CTRL_CSR           0x14
 #define QTN_CHIP_ID_MASK               0xF0
@@ -35,6 +35,8 @@
 /* FW names */
 
 #define QTN_PCI_PEARL_FW_NAME          "qtn/fmac_qsr10g.img"
+#define QTN_PCI_TOPAZ_FW_NAME          "qtn/fmac_qsr1000.img"
+#define QTN_PCI_TOPAZ_BOOTLD_NAME      "qtn/uboot_qsr1000.img"
 
 static inline unsigned int qtnf_chip_id_get(const void __iomem *regs_base)
 {
index e745733..3bc96b2 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "util.h"
+#include "qtn_hw_ids.h"
 
 void qtnf_sta_list_init(struct qtnf_sta_list *list)
 {
@@ -116,3 +117,20 @@ void qtnf_sta_list_free(struct qtnf_sta_list *list)
 
        INIT_LIST_HEAD(&list->head);
 }
+
+const char *qtnf_chipid_to_string(unsigned long chip_id)
+{
+       switch (chip_id) {
+       case QTN_CHIP_ID_TOPAZ:
+               return "Topaz";
+       case QTN_CHIP_ID_PEARL:
+               return "Pearl revA";
+       case QTN_CHIP_ID_PEARL_B:
+               return "Pearl revB";
+       case QTN_CHIP_ID_PEARL_C:
+               return "Pearl revC";
+       default:
+               return "unknown";
+       }
+}
+EXPORT_SYMBOL_GPL(qtnf_chipid_to_string);
index 0d4d92b..b8744ba 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/kernel.h>
 #include "core.h"
 
+const char *qtnf_chipid_to_string(unsigned long chip_id);
+
 void qtnf_sta_list_init(struct qtnf_sta_list *list);
 
 struct qtnf_sta_node *qtnf_sta_list_lookup(struct qtnf_sta_list *list,
index 0bc8b02..49a7327 100644 (file)
@@ -1302,7 +1302,7 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev,
                        break;
                case 2: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 1ff5434..e8e7bfe 100644 (file)
@@ -1430,7 +1430,7 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev,
                        break;
                case 2: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 9e7b893..0e95555 100644 (file)
@@ -2482,6 +2482,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
                switch (rt2x00dev->default_ant.tx_chain_num) {
                case 1:
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+                       /* fall through */
                case 2:
                        rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
                        break;
@@ -2490,6 +2491,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
                switch (rt2x00dev->default_ant.rx_chain_num) {
                case 1:
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+                       /* fall through */
                case 2:
                        rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
                        break;
@@ -9457,8 +9459,10 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        switch (rx_chains) {
        case 3:
                spec->ht.mcs.rx_mask[2] = 0xff;
+               /* fall through */
        case 2:
                spec->ht.mcs.rx_mask[1] = 0xff;
+               /* fall through */
        case 1:
                spec->ht.mcs.rx_mask[0] = 0xff;
                spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */
index cb0e119..4c5de8f 100644 (file)
@@ -2226,7 +2226,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
                        break;
                case 6: /* Failure, excessive retries */
                        __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags);
-                       /* Don't break, this is a failed frame! */
+                       /* Fall through - this is a failed frame! */
                default: /* Failure */
                        __set_bit(TXDONE_FAILURE, &txdesc.flags);
                }
index 08c607c..33ad875 100644 (file)
@@ -889,8 +889,10 @@ static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
        switch (ccsindex = get_free_tx_ccs(local)) {
        case ECCSBUSY:
                pr_debug("ray_hw_xmit tx_ccs table busy\n");
+               /* fall through */
        case ECCSFULL:
                pr_debug("ray_hw_xmit No free tx ccs\n");
+               /* fall through */
        case ECARDGONE:
                netif_stop_queue(dev);
                return XMIT_NO_CCS;
index 56040b1..2bd4305 100644 (file)
@@ -1153,6 +1153,7 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw)
        switch (hw->conf.chandef.width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
                ht = false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_20:
                opmode |= BW_OPMODE_20MHZ;
                rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
@@ -1280,6 +1281,7 @@ void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw)
        switch (hw->conf.chandef.width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
                ht = false;
+               /* fall through */
        case NL80211_CHAN_WIDTH_20:
                rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_20;
                subchannel = 0;
@@ -1748,9 +1750,11 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv)
                case 3:
                        priv->ep_tx_low_queue = 1;
                        priv->ep_tx_count++;
+                       /* fall through */
                case 2:
                        priv->ep_tx_normal_queue = 1;
                        priv->ep_tx_count++;
+                       /* fall through */
                case 1:
                        priv->ep_tx_high_queue = 1;
                        priv->ep_tx_count++;
@@ -5688,6 +5692,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               break;
        default:
                return -EOPNOTSUPP;
        }
index 6fbf884..d748aab 100644 (file)
@@ -292,11 +292,9 @@ bool halbtc_send_bt_mp_operation(struct btc_coexist *btcoexist, u8 op_code,
 static void halbtc_leave_lps(struct btc_coexist *btcoexist)
 {
        struct rtl_priv *rtlpriv;
-       struct rtl_ps_ctl *ppsc;
        bool ap_enable = false;
 
        rtlpriv = btcoexist->adapter;
-       ppsc = rtl_psc(rtlpriv);
 
        btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
                           &ap_enable);
@@ -315,11 +313,9 @@ static void halbtc_leave_lps(struct btc_coexist *btcoexist)
 static void halbtc_enter_lps(struct btc_coexist *btcoexist)
 {
        struct rtl_priv *rtlpriv;
-       struct rtl_ps_ctl *ppsc;
        bool ap_enable = false;
 
        rtlpriv = btcoexist->adapter;
-       ppsc = rtl_psc(rtlpriv);
 
        btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE,
                           &ap_enable);
index 4c1f8b0..14dcb08 100644 (file)
@@ -29,7 +29,6 @@
 #include "../stats.h"
 #include "reg.h"
 #include "def.h"
-#include "phy.h"
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
index 85cedd0..75bfa9d 100644 (file)
@@ -173,7 +173,7 @@ static int _rtl92d_fw_init(struct ieee80211_hw *hw)
                         rtl_read_byte(rtlpriv, FW_MAC1_READY));
        }
        RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG,
-                "Polling FW ready fail!! REG_MCUFWDL:0x%08ul\n",
+                "Polling FW ready fail!! REG_MCUFWDL:0x%08x\n",
                 rtl_read_dword(rtlpriv, REG_MCUFWDL));
        return -1;
 }
index 5cf29f5..3f33278 100644 (file)
@@ -509,13 +509,10 @@ bool rtl8723e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
        int i;
        bool rtstatus = true;
        u32 *radioa_array_table;
-       u32 *radiob_array_table;
-       u16 radioa_arraylen, radiob_arraylen;
+       u16 radioa_arraylen;
 
        radioa_arraylen = RTL8723ERADIOA_1TARRAYLENGTH;
        radioa_array_table = RTL8723E_RADIOA_1TARRAY;
-       radiob_arraylen = RTL8723E_RADIOB_1TARRAYLENGTH;
-       radiob_array_table = RTL8723E_RADIOB_1TARRAY;
 
        rtstatus = true;
 
index 61e8604..1bbee0b 100644 (file)
@@ -475,10 +475,6 @@ u32 RTL8723E_RADIOA_1TARRAY[RTL8723ERADIOA_1TARRAYLENGTH] = {
        0x000, 0x00030159,
 };
 
-u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH] = {
-       0x0,
-};
-
 u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH] = {
        0x420, 0x00000080,
        0x423, 0x00000000,
index 57a548c..a044f3c 100644 (file)
@@ -36,8 +36,6 @@ extern u32 RTL8723EPHY_REG_1TARRAY[RTL8723E_PHY_REG_1TARRAY_LENGTH];
 extern u32 RTL8723EPHY_REG_ARRAY_PG[RTL8723E_PHY_REG_ARRAY_PGLENGTH];
 #define RTL8723ERADIOA_1TARRAYLENGTH           282
 extern u32 RTL8723E_RADIOA_1TARRAY[RTL8723ERADIOA_1TARRAYLENGTH];
-#define RTL8723E_RADIOB_1TARRAYLENGTH          1
-extern u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH];
 #define RTL8723E_MACARRAYLENGTH                        172
 extern u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH];
 #define RTL8723E_AGCTAB_1TARRAYLENGTH          320
index 176deb2..a75451c 100644 (file)
@@ -394,6 +394,7 @@ static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw)
                        rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
                        break;
                }
+               /* fall through */
        case 0:
        case 2:
        default:
index d7960dd..b01c3c5 100644 (file)
@@ -29,7 +29,6 @@
 #include "../stats.h"
 #include "reg.h"
 #include "def.h"
-#include "phy.h"
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
index 612c211..449f6d2 100644 (file)
@@ -210,7 +210,7 @@ int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
        }
 
        /* This tells SDIO FIFO when to start read to host */
-       rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__);
+       rsi_dbg(INIT_ZONE, "%s: Initializing SDIO read start level\n", __func__);
        byte = 0x24;
 
        status = rsi_sdio_write_register(adapter,
@@ -223,7 +223,7 @@ int rsi_init_sdio_slave_regs(struct rsi_hw *adapter)
                return -1;
        }
 
-       rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__);
+       rsi_dbg(INIT_ZONE, "%s: Initializing FIFO ctrl registers\n", __func__);
        byte = (128 - 32);
 
        status = rsi_sdio_write_register(adapter,
index 38678e9..8dae92a 100644 (file)
@@ -1123,7 +1123,7 @@ int cw1200_setup_mac(struct cw1200_common *priv)
         *
         * NOTE2: RSSI based reports have been switched to RCPI, since
         * FW has a bug and RSSI reported values are not stable,
-        * what can leads to signal level oscilations in user-end applications
+        * what can lead to signal level oscilations in user-end applications
         */
        struct wsm_rcpi_rssi_threshold threshold = {
                .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
index dbe78d8..7f34ec0 100644 (file)
@@ -70,7 +70,7 @@ wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
 out:
        mutex_unlock(&wl->mutex);
 
-       return 0;
+       return ret;
 }
 
 static int
index 2534038..22c70f1 100644 (file)
@@ -969,6 +969,7 @@ static int zd1201_set_mode(struct net_device *dev,
                         */
                        zd1201_join(zd, "\0-*#\0", 5);
                        /* Put port in pIBSS */
+                       /* Fall through */
                case 8: /* No pseudo-IBSS in wireless extensions (yet) */
                        porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
                        break;
index f17f602..a8303af 100644 (file)
@@ -337,8 +337,6 @@ static void xennet_alloc_rx_buffers(struct netfront_queue *queue)
                return;
        }
 
-       wmb();          /* barrier so backend seens requests */
-
        RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->rx, notify);
        if (notify)
                notify_remote_via_irq(queue->rx_irq);
index 53189d4..810ab0f 100644 (file)
@@ -81,42 +81,3 @@ const void *of_get_mac_address(struct device_node *np)
        return of_get_mac_addr(np, "address");
 }
 EXPORT_SYMBOL(of_get_mac_address);
-
-/**
- * Obtain the MAC address from an nvmem provider named 'mac-address' through
- * device tree.
- * On success, copies the new address into memory pointed to by addr and
- * returns 0. Returns a negative error code otherwise.
- * @np:                Device tree node containing the nvmem-cells phandle
- * @addr:      Pointer to receive the MAC address using ether_addr_copy()
- */
-int of_get_nvmem_mac_address(struct device_node *np, void *addr)
-{
-       struct nvmem_cell *cell;
-       const void *mac;
-       size_t len;
-       int ret;
-
-       cell = of_nvmem_cell_get(np, "mac-address");
-       if (IS_ERR(cell))
-               return PTR_ERR(cell);
-
-       mac = nvmem_cell_read(cell, &len);
-
-       nvmem_cell_put(cell);
-
-       if (IS_ERR(mac))
-               return PTR_ERR(mac);
-
-       if (len < ETH_ALEN || !is_valid_ether_addr(mac)) {
-               ret = -EINVAL;
-       } else {
-               ether_addr_copy(addr, mac);
-               ret = 0;
-       }
-
-       kfree(mac);
-
-       return ret;
-}
-EXPORT_SYMBOL(of_get_nvmem_mac_address);
index 2012551..797fab3 100644 (file)
@@ -121,18 +121,20 @@ int ptp_open(struct posix_clock *pc, fmode_t fmode)
 
 long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
 {
-       struct ptp_clock_caps caps;
-       struct ptp_clock_request req;
-       struct ptp_sys_offset *sysoff = NULL;
-       struct ptp_sys_offset_precise precise_offset;
-       struct ptp_pin_desc pd;
        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+       struct ptp_sys_offset_extended *extoff = NULL;
+       struct ptp_sys_offset_precise precise_offset;
+       struct system_device_crosststamp xtstamp;
        struct ptp_clock_info *ops = ptp->info;
+       struct ptp_sys_offset *sysoff = NULL;
+       struct ptp_system_timestamp sts;
+       struct ptp_clock_request req;
+       struct ptp_clock_caps caps;
        struct ptp_clock_time *pct;
+       unsigned int i, pin_index;
+       struct ptp_pin_desc pd;
        struct timespec64 ts;
-       struct system_device_crosststamp xtstamp;
        int enable, err = 0;
-       unsigned int i, pin_index;
 
        switch (cmd) {
 
@@ -211,6 +213,36 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                        err = -EFAULT;
                break;
 
+       case PTP_SYS_OFFSET_EXTENDED:
+               if (!ptp->info->gettimex64) {
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+               extoff = memdup_user((void __user *)arg, sizeof(*extoff));
+               if (IS_ERR(extoff)) {
+                       err = PTR_ERR(extoff);
+                       extoff = NULL;
+                       break;
+               }
+               if (extoff->n_samples > PTP_MAX_SAMPLES) {
+                       err = -EINVAL;
+                       break;
+               }
+               for (i = 0; i < extoff->n_samples; i++) {
+                       err = ptp->info->gettimex64(ptp->info, &ts, &sts);
+                       if (err)
+                               goto out;
+                       extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
+                       extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
+                       extoff->ts[i][1].sec = ts.tv_sec;
+                       extoff->ts[i][1].nsec = ts.tv_nsec;
+                       extoff->ts[i][2].sec = sts.post_ts.tv_sec;
+                       extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
+               }
+               if (copy_to_user((void __user *)arg, extoff, sizeof(*extoff)))
+                       err = -EFAULT;
+               break;
+
        case PTP_SYS_OFFSET:
                sysoff = memdup_user((void __user *)arg, sizeof(*sysoff));
                if (IS_ERR(sysoff)) {
@@ -228,7 +260,12 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                        pct->sec = ts.tv_sec;
                        pct->nsec = ts.tv_nsec;
                        pct++;
-                       ptp->info->gettime64(ptp->info, &ts);
+                       if (ops->gettimex64)
+                               err = ops->gettimex64(ops, &ts, NULL);
+                       else
+                               err = ops->gettime64(ops, &ts);
+                       if (err)
+                               goto out;
                        pct->sec = ts.tv_sec;
                        pct->nsec = ts.tv_nsec;
                        pct++;
@@ -281,6 +318,8 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                break;
        }
 
+out:
+       kfree(extoff);
        kfree(sysoff);
        return err;
 }
index 5419a89..8a81eec 100644 (file)
@@ -117,7 +117,10 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec64 *tp)
        struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
        int err;
 
-       err = ptp->info->gettime64(ptp->info, tp);
+       if (ptp->info->gettimex64)
+               err = ptp->info->gettimex64(ptp->info, tp, NULL);
+       else
+               err = ptp->info->gettime64(ptp->info, tp);
        return err;
 }
 
@@ -249,8 +252,10 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
        ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid,
                                             ptp, ptp->pin_attr_groups,
                                             "ptp%d", ptp->index);
-       if (IS_ERR(ptp->dev))
+       if (IS_ERR(ptp->dev)) {
+               err = PTR_ERR(ptp->dev);
                goto no_device;
+       }
 
        /* Register a new PPS source. */
        if (info->pps) {
@@ -261,6 +266,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
                pps.owner = info->owner;
                ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
                if (!ptp->pps_source) {
+                       err = -EINVAL;
                        pr_err("failed to register pps source\n");
                        goto no_pps;
                }
index de21f62..183fc42 100644 (file)
@@ -214,6 +214,16 @@ static u64 rproc_virtio_get_features(struct virtio_device *vdev)
        return rsc->dfeatures;
 }
 
+static void rproc_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on remoteproc for now,
+        * because remoteproc uses vring_new_virtqueue() which
+        * creates virtio rings on preallocated memory.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int rproc_virtio_finalize_features(struct virtio_device *vdev)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
@@ -224,6 +234,9 @@ static int rproc_virtio_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features */
        vring_transport_features(vdev);
 
+       /* Give virtio_rproc a chance to accept features. */
+       rproc_transport_features(vdev);
+
        /* Make sure we don't have any features > 32 bits! */
        BUG_ON((u32)vdev->features != vdev->features);
 
index 04e294d..0ee0269 100644 (file)
@@ -314,7 +314,7 @@ struct qeth_hdr_layer3 {
        __u16 frame_offset;
        union {
                /* TX: */
-               u8 ipv6_addr[16];
+               struct in6_addr ipv6_addr;
                struct ipv4 {
                        u8 res[12];
                        u32 addr;
@@ -665,7 +665,6 @@ struct qeth_card_blkt {
 
 #define QETH_BROADCAST_WITH_ECHO    0x01
 #define QETH_BROADCAST_WITHOUT_ECHO 0x02
-#define QETH_LAYER2_MAC_READ       0x01
 #define QETH_LAYER2_MAC_REGISTERED  0x02
 struct qeth_card_info {
        unsigned short unit_addr2;
@@ -775,7 +774,6 @@ struct qeth_switch_info {
 #define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
 
 struct qeth_card {
-       struct list_head list;
        enum qeth_card_states state;
        spinlock_t lock;
        struct ccwgroup_device *gdev;
@@ -827,11 +825,6 @@ struct qeth_card {
        struct work_struct close_dev_work;
 };
 
-struct qeth_card_list_struct {
-       struct list_head list;
-       rwlock_t rwlock;
-};
-
 struct qeth_trap_id {
        __u16 lparnr;
        char vmname[8];
@@ -978,11 +971,11 @@ int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);
 void qeth_core_free_discipline(struct qeth_card *);
 
 /* exports for qeth discipline device drivers */
-extern struct qeth_card_list_struct qeth_core_card_list;
 extern struct kmem_cache *qeth_core_header_cache;
 extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS];
 
 struct net_device *qeth_clone_netdev(struct net_device *orig);
+struct qeth_card *qeth_get_card_by_busid(char *bus_id);
 void qeth_set_recovery_task(struct qeth_card *);
 void qeth_clear_recovery_task(struct qeth_card *);
 void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int);
@@ -1025,9 +1018,6 @@ 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);
 unsigned int qeth_count_elements(struct sk_buff *skb, unsigned int data_offset);
-int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb,
-                            struct qeth_hdr *hdr, unsigned int offset,
-                            unsigned int hd_len);
 int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                        struct sk_buff *skb, struct qeth_hdr *hdr,
                        unsigned int offset, unsigned int hd_len,
@@ -1058,11 +1048,6 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features);
 int qeth_vm_request_mac(struct qeth_card *card);
-int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb,
-                      struct qeth_hdr **hdr, unsigned int hdr_len,
-                      unsigned int proto_len, unsigned int *elements);
-void qeth_fill_tso_ext(struct qeth_hdr_tso *hdr, unsigned int payload_len,
-                      struct sk_buff *skb, unsigned int proto_len);
 int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
              struct qeth_qdio_out_q *queue, int ipv, int cast_type,
              void (*fill_header)(struct qeth_card *card, struct qeth_hdr *hdr,
index 2540652..e63e031 100644 (file)
@@ -54,8 +54,6 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = {
 };
 EXPORT_SYMBOL_GPL(qeth_dbf);
 
-struct qeth_card_list_struct qeth_core_card_list;
-EXPORT_SYMBOL_GPL(qeth_core_card_list);
 struct kmem_cache *qeth_core_header_cache;
 EXPORT_SYMBOL_GPL(qeth_core_header_cache);
 static struct kmem_cache *qeth_qdio_outbuf_cache;
@@ -2837,6 +2835,17 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card,
        cmd->hdr.prot_version = prot;
 }
 
+void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob)
+{
+       u8 prot_type = qeth_mpc_select_prot_type(card);
+
+       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+       memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
+       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+}
+EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd);
+
 struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
                enum qeth_ipa_cmds ipacmd, enum qeth_prot_versions prot)
 {
@@ -2844,6 +2853,7 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
 
        iob = qeth_get_buffer(&card->write);
        if (iob) {
+               qeth_prepare_ipa_cmd(card, iob);
                qeth_fill_ipacmd_header(card, __ipa_cmd(iob), ipacmd, prot);
        } else {
                dev_warn(&card->gdev->dev,
@@ -2856,17 +2866,6 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card,
 }
 EXPORT_SYMBOL_GPL(qeth_get_ipacmd_buffer);
 
-void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob)
-{
-       u8 prot_type = qeth_mpc_select_prot_type(card);
-
-       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-       memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data), &prot_type, 1);
-       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
-              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-}
-EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd);
-
 /**
  * qeth_send_ipa_cmd() - send an IPA command
  *
@@ -2881,7 +2880,6 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
        int rc;
 
        QETH_CARD_TEXT(card, 4, "sendipa");
-       qeth_prepare_ipa_cmd(card, iob);
        rc = qeth_send_control_data(card, IPA_CMD_LENGTH,
                                                iob, reply_cb, reply_param);
        if (rc == -ETIME) {
@@ -3777,9 +3775,9 @@ EXPORT_SYMBOL_GPL(qeth_count_elements);
  * The number of needed buffer elements is returned in @elements.
  * Error to create the hdr is indicated by returning with < 0.
  */
-int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb,
-                      struct qeth_hdr **hdr, unsigned int hdr_len,
-                      unsigned int proto_len, unsigned int *elements)
+static int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb,
+                             struct qeth_hdr **hdr, unsigned int hdr_len,
+                             unsigned int proto_len, unsigned int *elements)
 {
        const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card);
        const unsigned int contiguous = proto_len ? proto_len : 1;
@@ -3849,7 +3847,6 @@ check_layout:
        skb_copy_from_linear_data(skb, ((char *)*hdr) + hdr_len, proto_len);
        return 0;
 }
-EXPORT_SYMBOL_GPL(qeth_add_hw_header);
 
 static void __qeth_fill_buffer(struct sk_buff *skb,
                               struct qeth_qdio_out_buffer *buf,
@@ -3972,9 +3969,9 @@ static int qeth_fill_buffer(struct qeth_qdio_out_q *queue,
        return flush_cnt;
 }
 
-int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb,
-                            struct qeth_hdr *hdr, unsigned int offset,
-                            unsigned int hd_len)
+static int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue,
+                                   struct sk_buff *skb, struct qeth_hdr *hdr,
+                                   unsigned int offset, unsigned int hd_len)
 {
        int index = queue->next_buf_to_fill;
        struct qeth_qdio_out_buffer *buffer = queue->bufs[index];
@@ -3990,7 +3987,6 @@ int qeth_do_send_packet_fast(struct qeth_qdio_out_q *queue, struct sk_buff *skb,
        qeth_flush_buffers(queue, index, 1);
        return 0;
 }
-EXPORT_SYMBOL_GPL(qeth_do_send_packet_fast);
 
 int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                        struct sk_buff *skb, struct qeth_hdr *hdr,
@@ -4082,8 +4078,9 @@ out:
 }
 EXPORT_SYMBOL_GPL(qeth_do_send_packet);
 
-void qeth_fill_tso_ext(struct qeth_hdr_tso *hdr, unsigned int payload_len,
-                      struct sk_buff *skb, unsigned int proto_len)
+static void qeth_fill_tso_ext(struct qeth_hdr_tso *hdr,
+                             unsigned int payload_len, struct sk_buff *skb,
+                             unsigned int proto_len)
 {
        struct qeth_hdr_ext_tso *ext = &hdr->ext;
 
@@ -4096,7 +4093,6 @@ void qeth_fill_tso_ext(struct qeth_hdr_tso *hdr, unsigned int payload_len,
        ext->mss = skb_shinfo(skb)->gso_size;
        ext->dg_hdr_len = proto_len;
 }
-EXPORT_SYMBOL_GPL(qeth_fill_tso_ext);
 
 int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
              struct qeth_qdio_out_q *queue, int ipv, int cast_type,
@@ -4119,7 +4115,7 @@ int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
                proto_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
        } else {
                hw_hdr_len = sizeof(struct qeth_hdr);
-               proto_len = IS_IQD(card) ? ETH_HLEN : 0;
+               proto_len = (IS_IQD(card) && IS_LAYER2(card)) ? ETH_HLEN : 0;
        }
 
        rc = skb_cow_head(skb, hw_hdr_len);
@@ -4235,16 +4231,18 @@ static int qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
                struct qeth_reply *reply, unsigned long data)
 {
        struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
+       struct qeth_ipacmd_setadpparms *adp_cmd;
 
        QETH_CARD_TEXT(card, 4, "chgmaccb");
        if (qeth_setadpparms_inspect_rc(cmd))
                return 0;
 
-       if (IS_LAYER3(card) || !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
-               ether_addr_copy(card->dev->dev_addr,
-                               cmd->data.setadapterparms.data.change_addr.addr);
-               card->info.mac_bits |= QETH_LAYER2_MAC_READ;
-       }
+       adp_cmd = &cmd->data.setadapterparms;
+       if (IS_LAYER2(card) && IS_OSD(card) && !IS_VM_NIC(card) &&
+           !(adp_cmd->hdr.flags & QETH_SETADP_FLAGS_VIRTUAL_MAC))
+               return 0;
+
+       ether_addr_copy(card->dev->dev_addr, adp_cmd->data.change_addr.addr);
        return 0;
 }
 
@@ -4499,9 +4497,6 @@ static int qeth_send_ipa_snmp_cmd(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "sendsnmp");
 
-       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
-              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
        /* adjust PDU length fields in IPA_PDU_HEADER */
        s1 = (u32) IPA_PDU_HEADER_SIZE + len;
        s2 = (u32) len;
@@ -5477,34 +5472,11 @@ struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
 }
 EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd);
 
-static int qeth_send_setassparms(struct qeth_card *card,
-                                struct qeth_cmd_buffer *iob, u16 len,
-                                long data, int (*reply_cb)(struct qeth_card *,
-                                                           struct qeth_reply *,
-                                                           unsigned long),
-                                void *reply_param)
-{
-       int rc;
-       struct qeth_ipa_cmd *cmd;
-
-       QETH_CARD_TEXT(card, 4, "sendassp");
-
-       cmd = __ipa_cmd(iob);
-       if (len <= sizeof(__u32))
-               cmd->data.setassparms.data.flags_32bit = (__u32) data;
-       else   /* (len > sizeof(__u32)) */
-               memcpy(&cmd->data.setassparms.data, (void *) data, len);
-
-       rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
-       return rc;
-}
-
 int qeth_send_simple_setassparms_prot(struct qeth_card *card,
                                      enum qeth_ipa_funcs ipa_func,
                                      u16 cmd_code, long data,
                                      enum qeth_prot_versions prot)
 {
-       int rc;
        int length = 0;
        struct qeth_cmd_buffer *iob;
 
@@ -5514,9 +5486,9 @@ int qeth_send_simple_setassparms_prot(struct qeth_card *card,
        iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, length, prot);
        if (!iob)
                return -ENOMEM;
-       rc = qeth_send_setassparms(card, iob, length, data,
-                                  qeth_setassparms_cb, NULL);
-       return rc;
+
+       __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (__u32) data;
+       return qeth_send_ipa_cmd(card, iob, qeth_setassparms_cb, NULL);
 }
 EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms_prot);
 
@@ -5803,9 +5775,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
                break;
        }
 
-       write_lock_irq(&qeth_core_card_list.rwlock);
-       list_add_tail(&card->list, &qeth_core_card_list.list);
-       write_unlock_irq(&qeth_core_card_list.rwlock);
        return 0;
 
 err_disc:
@@ -5830,9 +5799,6 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev)
                qeth_core_free_discipline(card);
        }
 
-       write_lock_irq(&qeth_core_card_list.rwlock);
-       list_del(&card->list);
-       write_unlock_irq(&qeth_core_card_list.rwlock);
        free_netdev(card->dev);
        qeth_core_free_card(card);
        put_device(&gdev->dev);
@@ -5947,6 +5913,21 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = {
        .restore = qeth_core_restore,
 };
 
+struct qeth_card *qeth_get_card_by_busid(char *bus_id)
+{
+       struct ccwgroup_device *gdev;
+       struct qeth_card *card;
+
+       gdev = get_ccwgroupdev_by_busid(&qeth_core_ccwgroup_driver, bus_id);
+       if (!gdev)
+               return NULL;
+
+       card = dev_get_drvdata(&gdev->dev);
+       put_device(&gdev->dev);
+       return card;
+}
+EXPORT_SYMBOL_GPL(qeth_get_card_by_busid);
+
 int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct qeth_card *card = dev->ml_priv;
@@ -6378,16 +6359,16 @@ static int qeth_ipa_checksum_run_cmd(struct qeth_card *card,
                                     enum qeth_prot_versions prot)
 {
        struct qeth_cmd_buffer *iob;
-       int rc = -ENOMEM;
 
        QETH_CARD_TEXT(card, 4, "chkdocmd");
        iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
                                       sizeof(__u32), prot);
-       if (iob)
-               rc = qeth_send_setassparms(card, iob, sizeof(__u32), data,
-                                          qeth_ipa_checksum_run_cmd_cb,
-                                          chksum_cb);
-       return rc;
+       if (!iob)
+               return -ENOMEM;
+
+       __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (__u32) data;
+       return qeth_send_ipa_cmd(card, iob, qeth_ipa_checksum_run_cmd_cb,
+                                chksum_cb);
 }
 
 static int qeth_send_checksum_on(struct qeth_card *card, int cstype,
@@ -6485,8 +6466,7 @@ static int qeth_set_tso_on(struct qeth_card *card,
        if (!iob)
                return -ENOMEM;
 
-       rc = qeth_send_setassparms(card, iob, 0, 0 /* unused */,
-                                  qeth_start_tso_cb, &tso_data);
+       rc = qeth_send_ipa_cmd(card, iob, qeth_start_tso_cb, &tso_data);
        if (rc)
                return rc;
 
@@ -6503,10 +6483,9 @@ static int qeth_set_tso_on(struct qeth_card *card,
        }
 
        /* enable TSO capability */
-       caps.supported = 0;
-       caps.enabled = QETH_IPA_LARGE_SEND_TCP;
-       rc = qeth_send_setassparms(card, iob, sizeof(caps), (long) &caps,
-                                  qeth_setassparms_get_caps_cb, &caps);
+       __ipa_cmd(iob)->data.setassparms.data.caps.enabled =
+               QETH_IPA_LARGE_SEND_TCP;
+       rc = qeth_send_ipa_cmd(card, iob, qeth_setassparms_get_caps_cb, &caps);
        if (rc) {
                qeth_set_tso_off(card, prot);
                return rc;
@@ -6685,8 +6664,6 @@ static int __init qeth_core_init(void)
        int rc;
 
        pr_info("loading core functions\n");
-       INIT_LIST_HEAD(&qeth_core_card_list.list);
-       rwlock_init(&qeth_core_card_list.rwlock);
 
        qeth_wq = create_singlethread_workqueue("qeth_wq");
        if (!qeth_wq) {
index e891c0b..16fc51a 100644 (file)
@@ -144,7 +144,6 @@ unsigned char IPA_PDU_HEADER[] = {
                sizeof(struct qeth_ipa_cmd) % 256,
        0x00, 0x00, 0x00, 0x40,
 };
-EXPORT_SYMBOL_GPL(IPA_PDU_HEADER);
 
 struct ipa_rc_msg {
        enum qeth_ipa_return_codes rc;
index 3e54be2..1ab3219 100644 (file)
@@ -80,7 +80,9 @@ enum qeth_card_types {
 };
 
 #define IS_IQD(card)   ((card)->info.type == QETH_CARD_TYPE_IQD)
+#define IS_OSD(card)   ((card)->info.type == QETH_CARD_TYPE_OSD)
 #define IS_OSN(card)   ((card)->info.type == QETH_CARD_TYPE_OSN)
+#define IS_VM_NIC(card)        ((card)->info.guestlan)
 
 #define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
 /* only the first two bytes are looked at in qeth_get_cardname_short */
@@ -529,17 +531,20 @@ struct qeth_query_switch_attributes {
        __u8  reserved3[8];
 };
 
+#define QETH_SETADP_FLAGS_VIRTUAL_MAC  0x80    /* for CHANGE_ADDR_READ_MAC */
+
 struct qeth_ipacmd_setadpparms_hdr {
-       __u32 supp_hw_cmds;
-       __u32 reserved1;
-       __u16 cmdlength;
-       __u16 reserved2;
-       __u32 command_code;
-       __u16 return_code;
-       __u8  used_total;
-       __u8  seq_no;
-       __u32 reserved3;
-} __attribute__ ((packed));
+       u32 supp_hw_cmds;
+       u32 reserved1;
+       u16 cmdlength;
+       u16 reserved2;
+       u32 command_code;
+       u16 return_code;
+       u8 used_total;
+       u8 seq_no;
+       u8 flags;
+       u8 reserved3[3];
+};
 
 struct qeth_ipacmd_setadpparms {
        struct qeth_ipacmd_setadpparms_hdr hdr;
@@ -828,10 +833,9 @@ enum qeth_ipa_arp_return_codes {
 extern const char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
 extern const char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
 
-#define QETH_SETASS_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \
-                              sizeof(struct qeth_ipacmd_setassparms_hdr))
-#define QETH_IPA_ARP_DATA_POS(buffer) (buffer + IPA_PDU_HEADER_SIZE + \
-                                      QETH_SETASS_BASE_LEN)
+#define QETH_SETASS_BASE_LEN (IPA_PDU_HEADER_SIZE + \
+                             sizeof(struct qeth_ipacmd_hdr) + \
+                             sizeof(struct qeth_ipacmd_setassparms_hdr))
 #define QETH_SETADP_BASE_LEN (sizeof(struct qeth_ipacmd_hdr) + \
                              sizeof(struct qeth_ipacmd_setadpparms_hdr))
 #define QETH_SNMP_SETADP_CMDLENGTH 16
index 2914a1a..f108d4b 100644 (file)
@@ -36,28 +36,6 @@ static void qeth_l2_vnicc_init(struct qeth_card *card);
 static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
                                          u32 *timeout);
 
-static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no)
-{
-       struct qeth_card *card;
-       struct net_device *ndev;
-       __u16 temp_dev_no;
-       unsigned long flags;
-       struct ccw_dev_id read_devid;
-
-       ndev = NULL;
-       memcpy(&temp_dev_no, read_dev_no, 2);
-       read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
-       list_for_each_entry(card, &qeth_core_card_list.list, list) {
-               ccw_device_get_id(CARD_RDEV(card), &read_devid);
-               if (read_devid.devno == temp_dev_no) {
-                       ndev = card->dev;
-                       break;
-               }
-       }
-       read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
-       return ndev;
-}
-
 static int qeth_setdelmac_makerc(struct qeth_card *card, int retcode)
 {
        int rc;
@@ -461,12 +439,9 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
                /* fall back to alternative mechanism: */
        }
 
-       if (card->info.type == QETH_CARD_TYPE_IQD ||
-           card->info.type == QETH_CARD_TYPE_OSM ||
-           card->info.type == QETH_CARD_TYPE_OSX ||
-           card->info.guestlan) {
+       if (!IS_OSN(card)) {
                rc = qeth_setadpparms_change_macaddr(card);
-               if (!rc)
+               if (!rc && is_valid_ether_addr(card->dev->dev_addr))
                        goto out;
                QETH_DBF_MESSAGE(2, "READ_MAC Assist failed on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
@@ -917,7 +892,8 @@ static int qeth_l2_setup_netdev(struct qeth_card *card, bool carrier_ok)
                                       PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1));
        }
 
-       qeth_l2_request_initial_mac(card);
+       if (!is_valid_ether_addr(card->dev->dev_addr))
+               qeth_l2_request_initial_mac(card);
        netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
        rc = register_netdev(card->dev);
        if (!rc && carrier_ok)
@@ -1031,7 +1007,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
                        qeth_l2_set_rx_mode(card->dev);
                } else {
                        rtnl_lock();
-                       dev_open(card->dev);
+                       dev_open(card->dev, NULL);
                        rtnl_unlock();
                }
        }
@@ -1288,13 +1264,16 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
                  int (*data_cb)(struct sk_buff *))
 {
        struct qeth_card *card;
+       char bus_id[16];
+       u16 devno;
 
-       *dev = qeth_l2_netdev_by_devno(read_dev_no);
-       if (*dev == NULL)
-               return -ENODEV;
-       card = (*dev)->ml_priv;
-       if (!card)
+       memcpy(&devno, read_dev_no, 2);
+       sprintf(bus_id, "0.0.%04x", devno);
+       card = qeth_get_card_by_busid(bus_id);
+       if (!card || !IS_OSN(card))
                return -ENODEV;
+       *dev = card->dev;
+
        QETH_CARD_TEXT(card, 2, "osnreg");
        if ((assist_cb == NULL) || (data_cb == NULL))
                return -EINVAL;
index f08b745..42a7cdc 100644 (file)
@@ -949,9 +949,6 @@ static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,
        if (cmd->hdr.return_code == 0)
                ether_addr_copy(card->dev->dev_addr,
                                cmd->data.create_destroy_addr.unique_id);
-       else
-               eth_random_addr(card->dev->dev_addr);
-
        return 0;
 }
 
@@ -1685,21 +1682,6 @@ out_error:
        return 0;
 }
 
-static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card,
-               struct qeth_cmd_buffer *iob, int len,
-               int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
-                       unsigned long),
-               void *reply_param)
-{
-       QETH_CARD_TEXT(card, 4, "sendarp");
-
-       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
-              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-       return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
-                                     reply_cb, reply_param);
-}
-
 static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
        enum qeth_prot_versions prot,
        struct qeth_arp_query_info *qinfo)
@@ -1719,11 +1701,9 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
                return -ENOMEM;
        cmd = __ipa_cmd(iob);
        cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
-       cmd->data.setassparms.data.query_arp.reply_bits = 0;
-       cmd->data.setassparms.data.query_arp.no_entries = 0;
-       rc = qeth_l3_send_ipa_arp_cmd(card, iob,
-                          QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
-                          qeth_l3_arp_query_cb, (void *)qinfo);
+       rc = qeth_send_control_data(card,
+                                   QETH_SETASS_BASE_LEN + QETH_ARP_CMD_LEN,
+                                   iob, qeth_l3_arp_query_cb, qinfo);
        if (rc)
                QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n",
                                 CARD_DEVID(card), rc);
@@ -1929,22 +1909,6 @@ static int qeth_l3_get_cast_type(struct sk_buff *skb)
        }
 }
 
-static void qeth_l3_fill_af_iucv_hdr(struct qeth_hdr *hdr, struct sk_buff *skb,
-                                    unsigned int data_len)
-{
-       char daddr[16];
-
-       hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
-       hdr->hdr.l3.length = data_len;
-       hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
-
-       memset(daddr, 0, sizeof(daddr));
-       daddr[0] = 0xfe;
-       daddr[1] = 0x80;
-       memcpy(&daddr[8], iucv_trans_hdr(skb)->destUserID, 8);
-       memcpy(hdr->hdr.l3.next_hop.ipv6_addr, daddr, 16);
-}
-
 static u8 qeth_l3_cast_type_to_flag(int cast_type)
 {
        if (cast_type == RTN_MULTICAST)
@@ -1960,6 +1924,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                                struct sk_buff *skb, int ipv, int cast_type,
                                unsigned int data_len)
 {
+       struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3;
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
 
        hdr->hdr.l3.length = data_len;
@@ -1968,6 +1933,15 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                hdr->hdr.l3.id = QETH_HEADER_TYPE_L3_TSO;
        } else {
                hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+
+               if (skb->protocol == htons(ETH_P_AF_IUCV)) {
+                       l3_hdr->flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
+                       l3_hdr->next_hop.ipv6_addr.s6_addr16[0] = htons(0xfe80);
+                       memcpy(&l3_hdr->next_hop.ipv6_addr.s6_addr32[2],
+                              iucv_trans_hdr(skb)->destUserID, 8);
+                       return;
+               }
+
                if (skb->ip_summed == CHECKSUM_PARTIAL) {
                        qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, ipv);
                        /* some HW requires combined L3+L4 csum offload: */
@@ -2012,13 +1986,11 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
        } else {
                /* IPv6 */
                const struct rt6_info *rt = skb_rt6_info(skb);
-               const struct in6_addr *next_hop;
 
                if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
-                       next_hop = &rt->rt6i_gateway;
+                       l3_hdr->next_hop.ipv6_addr = rt->rt6i_gateway;
                else
-                       next_hop = &ipv6_hdr(skb)->daddr;
-               memcpy(hdr->hdr.l3.next_hop.ipv6_addr, next_hop, 16);
+                       l3_hdr->next_hop.ipv6_addr = ipv6_hdr(skb)->daddr;
 
                hdr->hdr.l3.flags |= QETH_HDR_IPV6;
                if (card->info.type != QETH_CARD_TYPE_IQD)
@@ -2044,84 +2016,25 @@ static void qeth_l3_fixup_headers(struct sk_buff *skb)
 static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
                        struct qeth_qdio_out_q *queue, int ipv, int cast_type)
 {
-       unsigned int hw_hdr_len, proto_len, frame_len, elements;
        unsigned char eth_hdr[ETH_HLEN];
-       bool is_tso = skb_is_gso(skb);
-       unsigned int data_offset = 0;
-       struct qeth_hdr *hdr = NULL;
-       unsigned int hd_len = 0;
-       int push_len, rc;
-       bool is_sg;
-
-       if (is_tso) {
-               hw_hdr_len = sizeof(struct qeth_hdr_tso);
-               proto_len = skb_transport_offset(skb) + tcp_hdrlen(skb) -
-                           ETH_HLEN;
-       } else {
-               hw_hdr_len = sizeof(struct qeth_hdr);
-               proto_len = 0;
-       }
+       unsigned int hw_hdr_len;
+       int rc;
 
        /* re-use the L2 header area for the HW header: */
+       hw_hdr_len = skb_is_gso(skb) ? sizeof(struct qeth_hdr_tso) :
+                                      sizeof(struct qeth_hdr);
        rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN);
        if (rc)
                return rc;
        skb_copy_from_linear_data(skb, eth_hdr, ETH_HLEN);
        skb_pull(skb, ETH_HLEN);
-       frame_len = skb->len;
 
        qeth_l3_fixup_headers(skb);
-       push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, proto_len,
-                                     &elements);
-       if (push_len < 0)
-               return push_len;
-       if (is_tso || !push_len) {
-               /* HW header needs its own buffer element. */
-               hd_len = hw_hdr_len + proto_len;
-               data_offset = push_len + proto_len;
-       }
-       memset(hdr, 0, hw_hdr_len);
-
-       if (skb->protocol == htons(ETH_P_AF_IUCV)) {
-               qeth_l3_fill_af_iucv_hdr(hdr, skb, frame_len);
-       } else {
-               qeth_l3_fill_header(card, hdr, skb, ipv, cast_type, frame_len);
-               if (is_tso)
-                       qeth_fill_tso_ext((struct qeth_hdr_tso *) hdr,
-                                         frame_len - proto_len, skb,
-                                         proto_len);
-       }
-
-       is_sg = skb_is_nonlinear(skb);
-       if (IS_IQD(card)) {
-               rc = qeth_do_send_packet_fast(queue, skb, hdr, data_offset,
-                                             hd_len);
-       } else {
-               /* TODO: drop skb_orphan() once TX completion is fast enough */
-               skb_orphan(skb);
-               rc = qeth_do_send_packet(card, queue, skb, hdr, data_offset,
-                                        hd_len, elements);
-       }
-
-       if (!rc) {
-               if (card->options.performance_stats) {
-                       card->perf_stats.buf_elements_sent += elements;
-                       if (is_sg)
-                               card->perf_stats.sg_skbs_sent++;
-                       if (is_tso) {
-                               card->perf_stats.large_send_bytes += frame_len;
-                               card->perf_stats.large_send_cnt++;
-                       }
-               }
-       } else {
-               if (!push_len)
-                       kmem_cache_free(qeth_core_header_cache, hdr);
-               if (rc == -EBUSY) {
-                       /* roll back to ETH header */
-                       skb_pull(skb, push_len);
-                       skb_push(skb, ETH_HLEN);
-                       skb_copy_to_linear_data(skb, eth_hdr, ETH_HLEN);
-               }
+       rc = qeth_xmit(card, skb, queue, ipv, cast_type, qeth_l3_fill_header);
+       if (rc == -EBUSY) {
+               /* roll back to ETH header */
+               skb_push(skb, ETH_HLEN);
+               skb_copy_to_linear_data(skb, eth_hdr, ETH_HLEN);
        }
        return rc;
 }
@@ -2366,9 +2279,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card, bool carrier_ok)
                rc = qeth_l3_iqd_read_initial_mac(card);
                if (rc)
                        goto out;
-
-               if (card->options.hsuid[0])
-                       memcpy(card->dev->perm_addr, card->options.hsuid, 9);
        } else
                return -ENODEV;
 
@@ -2507,7 +2417,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
                        __qeth_l3_open(card->dev);
                        qeth_l3_set_rx_mode(card->dev);
                } else {
-                       dev_open(card->dev);
+                       dev_open(card->dev, NULL);
                }
                rtnl_unlock();
        }
index c9c57b4..fc9dbad 100644 (file)
@@ -769,6 +769,17 @@ out_free:
        return rc;
 }
 
+static void ccw_transport_features(struct virtio_device *vdev)
+{
+       /*
+        * Packed ring isn't enabled on virtio_ccw for now,
+        * because virtio_ccw uses some legacy accessors,
+        * e.g. virtqueue_get_avail() and virtqueue_get_used()
+        * which aren't available in packed ring currently.
+        */
+       __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
+}
+
 static int virtio_ccw_finalize_features(struct virtio_device *vdev)
 {
        struct virtio_ccw_device *vcdev = to_vc_device(vdev);
@@ -795,6 +806,9 @@ static int virtio_ccw_finalize_features(struct virtio_device *vdev)
        /* Give virtio_ring a chance to accept features. */
        vring_transport_features(vdev);
 
+       /* Give virtio_ccw a chance to accept features. */
+       ccw_transport_features(vdev);
+
        features->index = 0;
        features->features = cpu_to_le32((u32)vdev->features);
        /* Write the first half of the feature bits to the host. */
index 064ef57..907dd87 100644 (file)
@@ -1767,8 +1767,7 @@ static int init_act_open(struct cxgbi_sock *csk)
                csk->mtu = dst_mtu(csk->dst);
        cxgb4_best_mtu(lldi->mtus, csk->mtu, &csk->mss_idx);
        csk->tx_chan = cxgb4_port_chan(ndev);
-       csk->smac_idx = cxgb4_tp_smt_idx(lldi->adapter_type,
-                                        cxgb4_port_viid(ndev));
+       csk->smac_idx = ((struct port_info *)netdev_priv(ndev))->smt_idx;
        step = lldi->ntxq / lldi->nchan;
        csk->txq_idx = cxgb4_port_idx(ndev) * step;
        step = lldi->nrxq / lldi->nchan;
index 5ce2471..52c153c 100644 (file)
@@ -36,6 +36,8 @@
 #define MAX_IRQNAME    16      /* big enough for "QMan portal %d" */
 #define QMAN_POLL_LIMIT 32
 #define QMAN_PIRQ_DQRR_ITHRESH 12
+#define QMAN_DQRR_IT_MAX 15
+#define QMAN_ITP_MAX 0xFFF
 #define QMAN_PIRQ_MR_ITHRESH 4
 #define QMAN_PIRQ_IPERIOD 100
 
@@ -727,9 +729,15 @@ static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr)
        qm_out(portal, QM_REG_DQRR_VDQCR, vdqcr);
 }
 
-static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+static inline int qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
 {
+
+       if (ithresh > QMAN_DQRR_IT_MAX)
+               return -EINVAL;
+
        qm_out(portal, QM_REG_DQRR_ITR, ithresh);
+
+       return 0;
 }
 
 /* --- MR API --- */
@@ -1012,20 +1020,27 @@ static inline void put_affine_portal(void)
 
 static struct workqueue_struct *qm_portal_wq;
 
-void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
+int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
 {
+       int res;
+
        if (!portal)
-               return;
+               return -EINVAL;
+
+       res = qm_dqrr_set_ithresh(&portal->p, ithresh);
+       if (res)
+               return res;
 
-       qm_dqrr_set_ithresh(&portal->p, ithresh);
        portal->p.dqrr.ithresh = ithresh;
+
+       return 0;
 }
 EXPORT_SYMBOL(qman_dqrr_set_ithresh);
 
 void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh)
 {
        if (portal && ithresh)
-               *ithresh = portal->p.dqrr.ithresh;
+               *ithresh = qm_in(&portal->p, QM_REG_DQRR_ITR);
 }
 EXPORT_SYMBOL(qman_dqrr_get_ithresh);
 
@@ -1036,10 +1051,14 @@ void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod)
 }
 EXPORT_SYMBOL(qman_portal_get_iperiod);
 
-void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod)
+int qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod)
 {
-       if (portal)
-               qm_out(&portal->p, QM_REG_ITPR, iperiod);
+       if (!portal || iperiod > QMAN_ITP_MAX)
+               return -EINVAL;
+
+       qm_out(&portal->p, QM_REG_ITPR, iperiod);
+
+       return 0;
 }
 EXPORT_SYMBOL(qman_portal_set_iperiod);
 
index 7a7ca67..daabace 100644 (file)
@@ -719,9 +719,6 @@ static int port_vlans_add(struct net_device *netdev,
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
        int vid, err = 0;
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
@@ -930,8 +927,6 @@ static int swdev_port_obj_del(struct net_device *netdev,
 static const struct switchdev_ops ethsw_port_switchdev_ops = {
        .switchdev_port_attr_get        = swdev_port_attr_get,
        .switchdev_port_attr_set        = swdev_port_attr_set,
-       .switchdev_port_obj_add         = swdev_port_obj_add,
-       .switchdev_port_obj_del         = swdev_port_obj_del,
 };
 
 /* For the moment, only flood setting needs to be updated */
@@ -972,6 +967,11 @@ static int port_bridge_leave(struct net_device *netdev)
        return err;
 }
 
+static bool ethsw_port_dev_check(const struct net_device *netdev)
+{
+       return netdev->netdev_ops == &ethsw_port_ops;
+}
+
 static int port_netdevice_event(struct notifier_block *unused,
                                unsigned long event, void *ptr)
 {
@@ -980,7 +980,7 @@ static int port_netdevice_event(struct notifier_block *unused,
        struct net_device *upper_dev;
        int err = 0;
 
-       if (netdev->netdev_ops != &ethsw_port_ops)
+       if (!ethsw_port_dev_check(netdev))
                return NOTIFY_DONE;
 
        /* Handle just upper dev link/unlink for the moment */
@@ -1083,10 +1083,51 @@ err_addr_alloc:
        return NOTIFY_BAD;
 }
 
+static int
+ethsw_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = swdev_port_obj_add(netdev, port_obj_info->obj,
+                                        port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = swdev_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int port_switchdev_blocking_event(struct notifier_block *unused,
+                                        unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!ethsw_port_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return ethsw_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block port_switchdev_nb = {
        .notifier_call = port_switchdev_event,
 };
 
+static struct notifier_block port_switchdev_blocking_nb = {
+       .notifier_call = port_switchdev_blocking_event,
+};
+
 static int ethsw_register_notifier(struct device *dev)
 {
        int err;
@@ -1103,8 +1144,16 @@ static int ethsw_register_notifier(struct device *dev)
                goto err_switchdev_nb;
        }
 
+       err = register_switchdev_blocking_notifier(&port_switchdev_blocking_nb);
+       if (err) {
+               dev_err(dev, "Failed to register switchdev blocking notifier\n");
+               goto err_switchdev_blocking_nb;
+       }
+
        return 0;
 
+err_switchdev_blocking_nb:
+       unregister_switchdev_notifier(&port_switchdev_nb);
 err_switchdev_nb:
        unregister_netdevice_notifier(&port_nb);
        return err;
@@ -1123,7 +1172,7 @@ static int ethsw_open(struct ethsw_core *ethsw)
 
        for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
                port_priv = ethsw->ports[i];
-               err = dev_open(port_priv->netdev);
+               err = dev_open(port_priv->netdev, NULL);
                if (err) {
                        netdev_err(port_priv->netdev, "dev_open err %d\n", err);
                        return err;
@@ -1291,8 +1340,15 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
 
 static void ethsw_unregister_notifier(struct device *dev)
 {
+       struct notifier_block *nb;
        int err;
 
+       nb = &port_switchdev_blocking_nb;
+       err = unregister_switchdev_blocking_notifier(nb);
+       if (err)
+               dev_err(dev,
+                       "Failed to unregister switchdev blocking notifier (%d)\n", err);
+
        err = unregister_switchdev_notifier(&port_switchdev_nb);
        if (err)
                dev_err(dev,
index 3647b8f..5eeb4b9 100644 (file)
@@ -2095,7 +2095,7 @@ static int visornic_resume(struct visor_device *dev,
        mod_timer(&devdata->irq_poll_timer, msecs_to_jiffies(2));
 
        rtnl_lock();
-       dev_open(netdev);
+       dev_open(netdev, NULL);
        rtnl_unlock();
 
        complete_func(dev, 0);
index 71888b9..0376d73 100644 (file)
@@ -932,8 +932,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip,
                        goto out;
                csk->mtu = ndev->mtu;
                csk->tx_chan = cxgb4_port_chan(ndev);
-               csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type,
-                                                cxgb4_port_viid(ndev));
+               csk->smac_idx =
+                              ((struct port_info *)netdev_priv(ndev))->smt_idx;
                step = cdev->lldi.ntxq /
                        cdev->lldi.nchan;
                csk->txq_idx = cxgb4_port_idx(ndev) * step;
@@ -968,8 +968,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip,
                port_id = cxgb4_port_idx(ndev);
                csk->mtu = dst_mtu(dst);
                csk->tx_chan = cxgb4_port_chan(ndev);
-               csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type,
-                                                cxgb4_port_viid(ndev));
+               csk->smac_idx =
+                              ((struct port_info *)netdev_priv(ndev))->smt_idx;
                step = cdev->lldi.ntxq /
                        cdev->lldi.nports;
                csk->txq_idx = (port_id * step) +
index ab11b2b..d919284 100644 (file)
@@ -141,6 +141,10 @@ struct vhost_net {
        unsigned tx_zcopy_err;
        /* Flush in progress. Protected by tx vq lock. */
        bool tx_flush;
+       /* Private page frag */
+       struct page_frag page_frag;
+       /* Refcount bias of page frag */
+       int refcnt_bias;
 };
 
 static unsigned vhost_net_zcopy_mask __read_mostly;
@@ -637,14 +641,53 @@ static bool tx_can_batch(struct vhost_virtqueue *vq, size_t total_len)
               !vhost_vq_avail_empty(vq->dev, vq);
 }
 
+#define SKB_FRAG_PAGE_ORDER     get_order(32768)
+
+static bool vhost_net_page_frag_refill(struct vhost_net *net, unsigned int sz,
+                                      struct page_frag *pfrag, gfp_t gfp)
+{
+       if (pfrag->page) {
+               if (pfrag->offset + sz <= pfrag->size)
+                       return true;
+               __page_frag_cache_drain(pfrag->page, net->refcnt_bias);
+       }
+
+       pfrag->offset = 0;
+       net->refcnt_bias = 0;
+       if (SKB_FRAG_PAGE_ORDER) {
+               /* Avoid direct reclaim but allow kswapd to wake */
+               pfrag->page = alloc_pages((gfp & ~__GFP_DIRECT_RECLAIM) |
+                                         __GFP_COMP | __GFP_NOWARN |
+                                         __GFP_NORETRY,
+                                         SKB_FRAG_PAGE_ORDER);
+               if (likely(pfrag->page)) {
+                       pfrag->size = PAGE_SIZE << SKB_FRAG_PAGE_ORDER;
+                       goto done;
+               }
+       }
+       pfrag->page = alloc_page(gfp);
+       if (likely(pfrag->page)) {
+               pfrag->size = PAGE_SIZE;
+               goto done;
+       }
+       return false;
+
+done:
+       net->refcnt_bias = USHRT_MAX;
+       page_ref_add(pfrag->page, USHRT_MAX - 1);
+       return true;
+}
+
 #define VHOST_NET_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD)
 
 static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
                               struct iov_iter *from)
 {
        struct vhost_virtqueue *vq = &nvq->vq;
+       struct vhost_net *net = container_of(vq->dev, struct vhost_net,
+                                            dev);
        struct socket *sock = vq->private_data;
-       struct page_frag *alloc_frag = &current->task_frag;
+       struct page_frag *alloc_frag = &net->page_frag;
        struct virtio_net_hdr *gso;
        struct xdp_buff *xdp = &nvq->xdp[nvq->batched_xdp];
        struct tun_xdp_hdr *hdr;
@@ -665,7 +708,8 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
 
        buflen += SKB_DATA_ALIGN(len + pad);
        alloc_frag->offset = ALIGN((u64)alloc_frag->offset, SMP_CACHE_BYTES);
-       if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
+       if (unlikely(!vhost_net_page_frag_refill(net, buflen,
+                                                alloc_frag, GFP_KERNEL)))
                return -ENOMEM;
 
        buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
@@ -703,7 +747,7 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
        xdp->data_end = xdp->data + len;
        hdr->buflen = buflen;
 
-       get_page(alloc_frag->page);
+       --net->refcnt_bias;
        alloc_frag->offset += buflen;
 
        ++nvq->batched_xdp;
@@ -1292,6 +1336,8 @@ static int vhost_net_open(struct inode *inode, struct file *f)
        vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, EPOLLIN, dev);
 
        f->private_data = n;
+       n->page_frag.page = NULL;
+       n->refcnt_bias = 0;
 
        return 0;
 }
@@ -1366,6 +1412,8 @@ static int vhost_net_release(struct inode *inode, struct file *f)
        kfree(n->vqs[VHOST_NET_VQ_RX].rxq.queue);
        kfree(n->vqs[VHOST_NET_VQ_TX].xdp);
        kfree(n->dev.vqs);
+       if (n->page_frag.page)
+               __page_frag_cache_drain(n->page_frag.page, n->refcnt_bias);
        kvfree(n);
        return 0;
 }
index 814b395..cd7e755 100644 (file)
        } while (0)
 #define END_USE(_vq) \
        do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; } while(0)
+#define LAST_ADD_TIME_UPDATE(_vq)                              \
+       do {                                                    \
+               ktime_t now = ktime_get();                      \
+                                                               \
+               /* No kick or get, with .1 second between?  Warn. */ \
+               if ((_vq)->last_add_time_valid)                 \
+                       WARN_ON(ktime_to_ms(ktime_sub(now,      \
+                               (_vq)->last_add_time)) > 100);  \
+               (_vq)->last_add_time = now;                     \
+               (_vq)->last_add_time_valid = true;              \
+       } while (0)
+#define LAST_ADD_TIME_CHECK(_vq)                               \
+       do {                                                    \
+               if ((_vq)->last_add_time_valid) {               \
+                       WARN_ON(ktime_to_ms(ktime_sub(ktime_get(), \
+                                     (_vq)->last_add_time)) > 100); \
+               }                                               \
+       } while (0)
+#define LAST_ADD_TIME_INVALID(_vq)                             \
+       ((_vq)->last_add_time_valid = false)
 #else
 #define BAD_RING(_vq, fmt, args...)                            \
        do {                                                    \
        } while (0)
 #define START_USE(vq)
 #define END_USE(vq)
+#define LAST_ADD_TIME_UPDATE(vq)
+#define LAST_ADD_TIME_CHECK(vq)
+#define LAST_ADD_TIME_INVALID(vq)
 #endif
 
-struct vring_desc_state {
+struct vring_desc_state_split {
        void *data;                     /* Data for callback. */
        struct vring_desc *indir_desc;  /* Indirect descriptor, if any. */
 };
 
+struct vring_desc_state_packed {
+       void *data;                     /* Data for callback. */
+       struct vring_packed_desc *indir_desc; /* Indirect descriptor, if any. */
+       u16 num;                        /* Descriptor list length. */
+       u16 next;                       /* The next desc state in a list. */
+       u16 last;                       /* The last desc state in a list. */
+};
+
+struct vring_desc_extra_packed {
+       dma_addr_t addr;                /* Buffer DMA addr. */
+       u32 len;                        /* Buffer length. */
+       u16 flags;                      /* Descriptor flags. */
+};
+
 struct vring_virtqueue {
        struct virtqueue vq;
 
-       /* Actual memory layout for this queue */
-       struct vring vring;
+       /* Is this a packed ring? */
+       bool packed_ring;
+
+       /* Is DMA API used? */
+       bool use_dma_api;
 
        /* Can we use weak barriers? */
        bool weak_barriers;
@@ -86,19 +126,70 @@ struct vring_virtqueue {
        /* Last used index we've seen. */
        u16 last_used_idx;
 
-       /* Last written value to avail->flags */
-       u16 avail_flags_shadow;
+       union {
+               /* Available for split ring */
+               struct {
+                       /* Actual memory layout for this queue. */
+                       struct vring vring;
+
+                       /* Last written value to avail->flags */
+                       u16 avail_flags_shadow;
+
+                       /*
+                        * Last written value to avail->idx in
+                        * guest byte order.
+                        */
+                       u16 avail_idx_shadow;
+
+                       /* Per-descriptor state. */
+                       struct vring_desc_state_split *desc_state;
 
-       /* Last written value to avail->idx in guest byte order */
-       u16 avail_idx_shadow;
+                       /* DMA address and size information */
+                       dma_addr_t queue_dma_addr;
+                       size_t queue_size_in_bytes;
+               } split;
+
+               /* Available for packed ring */
+               struct {
+                       /* Actual memory layout for this queue. */
+                       struct vring_packed vring;
+
+                       /* Driver ring wrap counter. */
+                       bool avail_wrap_counter;
+
+                       /* Device ring wrap counter. */
+                       bool used_wrap_counter;
+
+                       /* Avail used flags. */
+                       u16 avail_used_flags;
+
+                       /* Index of the next avail descriptor. */
+                       u16 next_avail_idx;
+
+                       /*
+                        * Last written value to driver->flags in
+                        * guest byte order.
+                        */
+                       u16 event_flags_shadow;
+
+                       /* Per-descriptor state. */
+                       struct vring_desc_state_packed *desc_state;
+                       struct vring_desc_extra_packed *desc_extra;
+
+                       /* DMA address and size information */
+                       dma_addr_t ring_dma_addr;
+                       dma_addr_t driver_event_dma_addr;
+                       dma_addr_t device_event_dma_addr;
+                       size_t ring_size_in_bytes;
+                       size_t event_size_in_bytes;
+               } packed;
+       };
 
        /* How to notify other side. FIXME: commonalize hcalls! */
        bool (*notify)(struct virtqueue *vq);
 
        /* DMA, allocation, and size information */
        bool we_own_ring;
-       size_t queue_size_in_bytes;
-       dma_addr_t queue_dma_addr;
 
 #ifdef DEBUG
        /* They're supposed to lock for us. */
@@ -108,13 +199,27 @@ struct vring_virtqueue {
        bool last_add_time_valid;
        ktime_t last_add_time;
 #endif
-
-       /* Per-descriptor state. */
-       struct vring_desc_state desc_state[];
 };
 
+
+/*
+ * Helpers.
+ */
+
 #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
 
+static inline bool virtqueue_use_indirect(struct virtqueue *_vq,
+                                         unsigned int total_sg)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       /*
+        * If the host supports indirect descriptor tables, and we have multiple
+        * buffers, then go indirect. FIXME: tune this threshold
+        */
+       return (vq->indirect && total_sg > 1 && vq->vq.num_free);
+}
+
 /*
  * Modern virtio devices have feature bits to specify whether they need a
  * quirk and bypass the IOMMU. If not there, just use the DMA API.
@@ -161,6 +266,48 @@ static bool vring_use_dma_api(struct virtio_device *vdev)
        return false;
 }
 
+static void *vring_alloc_queue(struct virtio_device *vdev, size_t size,
+                             dma_addr_t *dma_handle, gfp_t flag)
+{
+       if (vring_use_dma_api(vdev)) {
+               return dma_alloc_coherent(vdev->dev.parent, size,
+                                         dma_handle, flag);
+       } else {
+               void *queue = alloc_pages_exact(PAGE_ALIGN(size), flag);
+
+               if (queue) {
+                       phys_addr_t phys_addr = virt_to_phys(queue);
+                       *dma_handle = (dma_addr_t)phys_addr;
+
+                       /*
+                        * Sanity check: make sure we dind't truncate
+                        * the address.  The only arches I can find that
+                        * have 64-bit phys_addr_t but 32-bit dma_addr_t
+                        * are certain non-highmem MIPS and x86
+                        * configurations, but these configurations
+                        * should never allocate physical pages above 32
+                        * bits, so this is fine.  Just in case, throw a
+                        * warning and abort if we end up with an
+                        * unrepresentable address.
+                        */
+                       if (WARN_ON_ONCE(*dma_handle != phys_addr)) {
+                               free_pages_exact(queue, PAGE_ALIGN(size));
+                               return NULL;
+                       }
+               }
+               return queue;
+       }
+}
+
+static void vring_free_queue(struct virtio_device *vdev, size_t size,
+                            void *queue, dma_addr_t dma_handle)
+{
+       if (vring_use_dma_api(vdev))
+               dma_free_coherent(vdev->dev.parent, size, queue, dma_handle);
+       else
+               free_pages_exact(queue, PAGE_ALIGN(size));
+}
+
 /*
  * The DMA ops on various arches are rather gnarly right now, and
  * making all of the arch DMA ops work on the vring device itself
@@ -176,7 +323,7 @@ static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq,
                                   struct scatterlist *sg,
                                   enum dma_data_direction direction)
 {
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return (dma_addr_t)sg_phys(sg);
 
        /*
@@ -193,19 +340,33 @@ static dma_addr_t vring_map_single(const struct vring_virtqueue *vq,
                                   void *cpu_addr, size_t size,
                                   enum dma_data_direction direction)
 {
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return (dma_addr_t)virt_to_phys(cpu_addr);
 
        return dma_map_single(vring_dma_dev(vq),
                              cpu_addr, size, direction);
 }
 
-static void vring_unmap_one(const struct vring_virtqueue *vq,
-                           struct vring_desc *desc)
+static int vring_mapping_error(const struct vring_virtqueue *vq,
+                              dma_addr_t addr)
+{
+       if (!vq->use_dma_api)
+               return 0;
+
+       return dma_mapping_error(vring_dma_dev(vq), addr);
+}
+
+
+/*
+ * Split ring specific functions - *_split().
+ */
+
+static void vring_unmap_one_split(const struct vring_virtqueue *vq,
+                                 struct vring_desc *desc)
 {
        u16 flags;
 
-       if (!vring_use_dma_api(vq->vq.vdev))
+       if (!vq->use_dma_api)
                return;
 
        flags = virtio16_to_cpu(vq->vq.vdev, desc->flags);
@@ -225,17 +386,9 @@ static void vring_unmap_one(const struct vring_virtqueue *vq,
        }
 }
 
-static int vring_mapping_error(const struct vring_virtqueue *vq,
-                              dma_addr_t addr)
-{
-       if (!vring_use_dma_api(vq->vq.vdev))
-               return 0;
-
-       return dma_mapping_error(vring_dma_dev(vq), addr);
-}
-
-static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
-                                        unsigned int total_sg, gfp_t gfp)
+static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq,
+                                              unsigned int total_sg,
+                                              gfp_t gfp)
 {
        struct vring_desc *desc;
        unsigned int i;
@@ -256,14 +409,14 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
        return desc;
 }
 
-static inline int virtqueue_add(struct virtqueue *_vq,
-                               struct scatterlist *sgs[],
-                               unsigned int total_sg,
-                               unsigned int out_sgs,
-                               unsigned int in_sgs,
-                               void *data,
-                               void *ctx,
-                               gfp_t gfp)
+static inline int virtqueue_add_split(struct virtqueue *_vq,
+                                     struct scatterlist *sgs[],
+                                     unsigned int total_sg,
+                                     unsigned int out_sgs,
+                                     unsigned int in_sgs,
+                                     void *data,
+                                     void *ctx,
+                                     gfp_t gfp)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
        struct scatterlist *sg;
@@ -282,30 +435,17 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                return -EIO;
        }
 
-#ifdef DEBUG
-       {
-               ktime_t now = ktime_get();
-
-               /* No kick or get, with .1 second between?  Warn. */
-               if (vq->last_add_time_valid)
-                       WARN_ON(ktime_to_ms(ktime_sub(now, vq->last_add_time))
-                                           > 100);
-               vq->last_add_time = now;
-               vq->last_add_time_valid = true;
-       }
-#endif
+       LAST_ADD_TIME_UPDATE(vq);
 
        BUG_ON(total_sg == 0);
 
        head = vq->free_head;
 
-       /* If the host supports indirect descriptor tables, and we have multiple
-        * buffers, then go indirect. FIXME: tune this threshold */
-       if (vq->indirect && total_sg > 1 && vq->vq.num_free)
-               desc = alloc_indirect(_vq, total_sg, gfp);
+       if (virtqueue_use_indirect(_vq, total_sg))
+               desc = alloc_indirect_split(_vq, total_sg, gfp);
        else {
                desc = NULL;
-               WARN_ON_ONCE(total_sg > vq->vring.num && !vq->indirect);
+               WARN_ON_ONCE(total_sg > vq->split.vring.num && !vq->indirect);
        }
 
        if (desc) {
@@ -316,7 +456,7 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                descs_used = 1;
        } else {
                indirect = false;
-               desc = vq->vring.desc;
+               desc = vq->split.vring.desc;
                i = head;
                descs_used = total_sg;
        }
@@ -372,10 +512,13 @@ static inline int virtqueue_add(struct virtqueue *_vq,
                if (vring_mapping_error(vq, addr))
                        goto unmap_release;
 
-               vq->vring.desc[head].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_INDIRECT);
-               vq->vring.desc[head].addr = cpu_to_virtio64(_vq->vdev, addr);
+               vq->split.vring.desc[head].flags = cpu_to_virtio16(_vq->vdev,
+                               VRING_DESC_F_INDIRECT);
+               vq->split.vring.desc[head].addr = cpu_to_virtio64(_vq->vdev,
+                               addr);
 
-               vq->vring.desc[head].len = cpu_to_virtio32(_vq->vdev, total_sg * sizeof(struct vring_desc));
+               vq->split.vring.desc[head].len = cpu_to_virtio32(_vq->vdev,
+                               total_sg * sizeof(struct vring_desc));
        }
 
        /* We're using some buffers from the free list. */
@@ -383,27 +526,29 @@ static inline int virtqueue_add(struct virtqueue *_vq,
 
        /* Update free pointer */
        if (indirect)
-               vq->free_head = virtio16_to_cpu(_vq->vdev, vq->vring.desc[head].next);
+               vq->free_head = virtio16_to_cpu(_vq->vdev,
+                                       vq->split.vring.desc[head].next);
        else
                vq->free_head = i;
 
        /* Store token and indirect buffer state. */
-       vq->desc_state[head].data = data;
+       vq->split.desc_state[head].data = data;
        if (indirect)
-               vq->desc_state[head].indir_desc = desc;
+               vq->split.desc_state[head].indir_desc = desc;
        else
-               vq->desc_state[head].indir_desc = ctx;
+               vq->split.desc_state[head].indir_desc = ctx;
 
        /* Put entry in available array (but don't update avail->idx until they
         * do sync). */
-       avail = vq->avail_idx_shadow & (vq->vring.num - 1);
-       vq->vring.avail->ring[avail] = cpu_to_virtio16(_vq->vdev, head);
+       avail = vq->split.avail_idx_shadow & (vq->split.vring.num - 1);
+       vq->split.vring.avail->ring[avail] = cpu_to_virtio16(_vq->vdev, head);
 
        /* Descriptors and available array need to be set before we expose the
         * new available array entries. */
        virtio_wmb(vq->weak_barriers);
-       vq->avail_idx_shadow++;
-       vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
+       vq->split.avail_idx_shadow++;
+       vq->split.vring.avail->idx = cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_idx_shadow);
        vq->num_added++;
 
        pr_debug("Added buffer head %i to %p\n", head, vq);
@@ -423,8 +568,8 @@ unmap_release:
        for (n = 0; n < total_sg; n++) {
                if (i == err_idx)
                        break;
-               vring_unmap_one(vq, &desc[i]);
-               i = virtio16_to_cpu(_vq->vdev, vq->vring.desc[i].next);
+               vring_unmap_one_split(vq, &desc[i]);
+               i = virtio16_to_cpu(_vq->vdev, vq->split.vring.desc[i].next);
        }
 
        if (indirect)
@@ -434,6 +579,1122 @@ unmap_release:
        return -EIO;
 }
 
+static bool virtqueue_kick_prepare_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 new, old;
+       bool needs_kick;
+
+       START_USE(vq);
+       /* We need to expose available array entries before checking avail
+        * event. */
+       virtio_mb(vq->weak_barriers);
+
+       old = vq->split.avail_idx_shadow - vq->num_added;
+       new = vq->split.avail_idx_shadow;
+       vq->num_added = 0;
+
+       LAST_ADD_TIME_CHECK(vq);
+       LAST_ADD_TIME_INVALID(vq);
+
+       if (vq->event) {
+               needs_kick = vring_need_event(virtio16_to_cpu(_vq->vdev,
+                                       vring_avail_event(&vq->split.vring)),
+                                             new, old);
+       } else {
+               needs_kick = !(vq->split.vring.used->flags &
+                                       cpu_to_virtio16(_vq->vdev,
+                                               VRING_USED_F_NO_NOTIFY));
+       }
+       END_USE(vq);
+       return needs_kick;
+}
+
+static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head,
+                            void **ctx)
+{
+       unsigned int i, j;
+       __virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
+
+       /* Clear data ptr. */
+       vq->split.desc_state[head].data = NULL;
+
+       /* Put back on free list: unmap first-level descriptors and find end */
+       i = head;
+
+       while (vq->split.vring.desc[i].flags & nextflag) {
+               vring_unmap_one_split(vq, &vq->split.vring.desc[i]);
+               i = virtio16_to_cpu(vq->vq.vdev, vq->split.vring.desc[i].next);
+               vq->vq.num_free++;
+       }
+
+       vring_unmap_one_split(vq, &vq->split.vring.desc[i]);
+       vq->split.vring.desc[i].next = cpu_to_virtio16(vq->vq.vdev,
+                                               vq->free_head);
+       vq->free_head = head;
+
+       /* Plus final descriptor */
+       vq->vq.num_free++;
+
+       if (vq->indirect) {
+               struct vring_desc *indir_desc =
+                               vq->split.desc_state[head].indir_desc;
+               u32 len;
+
+               /* Free the indirect table, if any, now that it's unmapped. */
+               if (!indir_desc)
+                       return;
+
+               len = virtio32_to_cpu(vq->vq.vdev,
+                               vq->split.vring.desc[head].len);
+
+               BUG_ON(!(vq->split.vring.desc[head].flags &
+                        cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_INDIRECT)));
+               BUG_ON(len == 0 || len % sizeof(struct vring_desc));
+
+               for (j = 0; j < len / sizeof(struct vring_desc); j++)
+                       vring_unmap_one_split(vq, &indir_desc[j]);
+
+               kfree(indir_desc);
+               vq->split.desc_state[head].indir_desc = NULL;
+       } else if (ctx) {
+               *ctx = vq->split.desc_state[head].indir_desc;
+       }
+}
+
+static inline bool more_used_split(const struct vring_virtqueue *vq)
+{
+       return vq->last_used_idx != virtio16_to_cpu(vq->vq.vdev,
+                       vq->split.vring.used->idx);
+}
+
+static void *virtqueue_get_buf_ctx_split(struct virtqueue *_vq,
+                                        unsigned int *len,
+                                        void **ctx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       void *ret;
+       unsigned int i;
+       u16 last_used;
+
+       START_USE(vq);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return NULL;
+       }
+
+       if (!more_used_split(vq)) {
+               pr_debug("No more buffers in queue\n");
+               END_USE(vq);
+               return NULL;
+       }
+
+       /* Only get used array entries after they have been exposed by host. */
+       virtio_rmb(vq->weak_barriers);
+
+       last_used = (vq->last_used_idx & (vq->split.vring.num - 1));
+       i = virtio32_to_cpu(_vq->vdev,
+                       vq->split.vring.used->ring[last_used].id);
+       *len = virtio32_to_cpu(_vq->vdev,
+                       vq->split.vring.used->ring[last_used].len);
+
+       if (unlikely(i >= vq->split.vring.num)) {
+               BAD_RING(vq, "id %u out of range\n", i);
+               return NULL;
+       }
+       if (unlikely(!vq->split.desc_state[i].data)) {
+               BAD_RING(vq, "id %u is not a head!\n", i);
+               return NULL;
+       }
+
+       /* detach_buf_split clears data, so grab it now. */
+       ret = vq->split.desc_state[i].data;
+       detach_buf_split(vq, i, ctx);
+       vq->last_used_idx++;
+       /* If we expect an interrupt for the next entry, tell host
+        * by writing event index and flush out the write before
+        * the read in the next get_buf call. */
+       if (!(vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
+               virtio_store_mb(vq->weak_barriers,
+                               &vring_used_event(&vq->split.vring),
+                               cpu_to_virtio16(_vq->vdev, vq->last_used_idx));
+
+       LAST_ADD_TIME_INVALID(vq);
+
+       END_USE(vq);
+       return ret;
+}
+
+static void virtqueue_disable_cb_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       if (!(vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
+               vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+}
+
+static unsigned virtqueue_enable_cb_prepare_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 last_used_idx;
+
+       START_USE(vq);
+
+       /* We optimistically turn back on interrupts, then check if there was
+        * more to do. */
+       /* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
+        * either clear the flags bit or point the event index at the next
+        * entry. Always do both to keep code simple. */
+       if (vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+               vq->split.avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+       vring_used_event(&vq->split.vring) = cpu_to_virtio16(_vq->vdev,
+                       last_used_idx = vq->last_used_idx);
+       END_USE(vq);
+       return last_used_idx;
+}
+
+static bool virtqueue_poll_split(struct virtqueue *_vq, unsigned last_used_idx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       return (u16)last_used_idx != virtio16_to_cpu(_vq->vdev,
+                       vq->split.vring.used->idx);
+}
+
+static bool virtqueue_enable_cb_delayed_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 bufs;
+
+       START_USE(vq);
+
+       /* We optimistically turn back on interrupts, then check if there was
+        * more to do. */
+       /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
+        * either clear the flags bit or point the event index at the next
+        * entry. Always update the event index to keep code simple. */
+       if (vq->split.avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+               vq->split.avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+               if (!vq->event)
+                       vq->split.vring.avail->flags =
+                               cpu_to_virtio16(_vq->vdev,
+                                               vq->split.avail_flags_shadow);
+       }
+       /* TODO: tune this threshold */
+       bufs = (u16)(vq->split.avail_idx_shadow - vq->last_used_idx) * 3 / 4;
+
+       virtio_store_mb(vq->weak_barriers,
+                       &vring_used_event(&vq->split.vring),
+                       cpu_to_virtio16(_vq->vdev, vq->last_used_idx + bufs));
+
+       if (unlikely((u16)(virtio16_to_cpu(_vq->vdev, vq->split.vring.used->idx)
+                                       - vq->last_used_idx) > bufs)) {
+               END_USE(vq);
+               return false;
+       }
+
+       END_USE(vq);
+       return true;
+}
+
+static void *virtqueue_detach_unused_buf_split(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       unsigned int i;
+       void *buf;
+
+       START_USE(vq);
+
+       for (i = 0; i < vq->split.vring.num; i++) {
+               if (!vq->split.desc_state[i].data)
+                       continue;
+               /* detach_buf_split clears data, so grab it now. */
+               buf = vq->split.desc_state[i].data;
+               detach_buf_split(vq, i, NULL);
+               vq->split.avail_idx_shadow--;
+               vq->split.vring.avail->idx = cpu_to_virtio16(_vq->vdev,
+                               vq->split.avail_idx_shadow);
+               END_USE(vq);
+               return buf;
+       }
+       /* That should have freed everything. */
+       BUG_ON(vq->vq.num_free != vq->split.vring.num);
+
+       END_USE(vq);
+       return NULL;
+}
+
+static struct virtqueue *vring_create_virtqueue_split(
+       unsigned int index,
+       unsigned int num,
+       unsigned int vring_align,
+       struct virtio_device *vdev,
+       bool weak_barriers,
+       bool may_reduce_num,
+       bool context,
+       bool (*notify)(struct virtqueue *),
+       void (*callback)(struct virtqueue *),
+       const char *name)
+{
+       struct virtqueue *vq;
+       void *queue = NULL;
+       dma_addr_t dma_addr;
+       size_t queue_size_in_bytes;
+       struct vring vring;
+
+       /* We assume num is a power of 2. */
+       if (num & (num - 1)) {
+               dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num);
+               return NULL;
+       }
+
+       /* TODO: allocate each queue chunk individually */
+       for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
+               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
+                                         &dma_addr,
+                                         GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+               if (queue)
+                       break;
+       }
+
+       if (!num)
+               return NULL;
+
+       if (!queue) {
+               /* Try to get a single page. You are my only hope! */
+               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
+                                         &dma_addr, GFP_KERNEL|__GFP_ZERO);
+       }
+       if (!queue)
+               return NULL;
+
+       queue_size_in_bytes = vring_size(num, vring_align);
+       vring_init(&vring, num, queue, vring_align);
+
+       vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
+                                  notify, callback, name);
+       if (!vq) {
+               vring_free_queue(vdev, queue_size_in_bytes, queue,
+                                dma_addr);
+               return NULL;
+       }
+
+       to_vvq(vq)->split.queue_dma_addr = dma_addr;
+       to_vvq(vq)->split.queue_size_in_bytes = queue_size_in_bytes;
+       to_vvq(vq)->we_own_ring = true;
+
+       return vq;
+}
+
+
+/*
+ * Packed ring specific functions - *_packed().
+ */
+
+static void vring_unmap_state_packed(const struct vring_virtqueue *vq,
+                                    struct vring_desc_extra_packed *state)
+{
+       u16 flags;
+
+       if (!vq->use_dma_api)
+               return;
+
+       flags = state->flags;
+
+       if (flags & VRING_DESC_F_INDIRECT) {
+               dma_unmap_single(vring_dma_dev(vq),
+                                state->addr, state->len,
+                                (flags & VRING_DESC_F_WRITE) ?
+                                DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       } else {
+               dma_unmap_page(vring_dma_dev(vq),
+                              state->addr, state->len,
+                              (flags & VRING_DESC_F_WRITE) ?
+                              DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+}
+
+static void vring_unmap_desc_packed(const struct vring_virtqueue *vq,
+                                  struct vring_packed_desc *desc)
+{
+       u16 flags;
+
+       if (!vq->use_dma_api)
+               return;
+
+       flags = le16_to_cpu(desc->flags);
+
+       if (flags & VRING_DESC_F_INDIRECT) {
+               dma_unmap_single(vring_dma_dev(vq),
+                                le64_to_cpu(desc->addr),
+                                le32_to_cpu(desc->len),
+                                (flags & VRING_DESC_F_WRITE) ?
+                                DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       } else {
+               dma_unmap_page(vring_dma_dev(vq),
+                              le64_to_cpu(desc->addr),
+                              le32_to_cpu(desc->len),
+                              (flags & VRING_DESC_F_WRITE) ?
+                              DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+}
+
+static struct vring_packed_desc *alloc_indirect_packed(unsigned int total_sg,
+                                                      gfp_t gfp)
+{
+       struct vring_packed_desc *desc;
+
+       /*
+        * We require lowmem mappings for the descriptors because
+        * otherwise virt_to_phys will give us bogus addresses in the
+        * virtqueue.
+        */
+       gfp &= ~__GFP_HIGHMEM;
+
+       desc = kmalloc_array(total_sg, sizeof(struct vring_packed_desc), gfp);
+
+       return desc;
+}
+
+static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq,
+                                      struct scatterlist *sgs[],
+                                      unsigned int total_sg,
+                                      unsigned int out_sgs,
+                                      unsigned int in_sgs,
+                                      void *data,
+                                      gfp_t gfp)
+{
+       struct vring_packed_desc *desc;
+       struct scatterlist *sg;
+       unsigned int i, n, err_idx;
+       u16 head, id;
+       dma_addr_t addr;
+
+       head = vq->packed.next_avail_idx;
+       desc = alloc_indirect_packed(total_sg, gfp);
+
+       if (unlikely(vq->vq.num_free < 1)) {
+               pr_debug("Can't add buf len 1 - avail = 0\n");
+               END_USE(vq);
+               return -ENOSPC;
+       }
+
+       i = 0;
+       id = vq->free_head;
+       BUG_ON(id == vq->packed.vring.num);
+
+       for (n = 0; n < out_sgs + in_sgs; n++) {
+               for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+                       addr = vring_map_one_sg(vq, sg, n < out_sgs ?
+                                       DMA_TO_DEVICE : DMA_FROM_DEVICE);
+                       if (vring_mapping_error(vq, addr))
+                               goto unmap_release;
+
+                       desc[i].flags = cpu_to_le16(n < out_sgs ?
+                                               0 : VRING_DESC_F_WRITE);
+                       desc[i].addr = cpu_to_le64(addr);
+                       desc[i].len = cpu_to_le32(sg->length);
+                       i++;
+               }
+       }
+
+       /* Now that the indirect table is filled in, map it. */
+       addr = vring_map_single(vq, desc,
+                       total_sg * sizeof(struct vring_packed_desc),
+                       DMA_TO_DEVICE);
+       if (vring_mapping_error(vq, addr))
+               goto unmap_release;
+
+       vq->packed.vring.desc[head].addr = cpu_to_le64(addr);
+       vq->packed.vring.desc[head].len = cpu_to_le32(total_sg *
+                               sizeof(struct vring_packed_desc));
+       vq->packed.vring.desc[head].id = cpu_to_le16(id);
+
+       if (vq->use_dma_api) {
+               vq->packed.desc_extra[id].addr = addr;
+               vq->packed.desc_extra[id].len = total_sg *
+                               sizeof(struct vring_packed_desc);
+               vq->packed.desc_extra[id].flags = VRING_DESC_F_INDIRECT |
+                                                 vq->packed.avail_used_flags;
+       }
+
+       /*
+        * A driver MUST NOT make the first descriptor in the list
+        * available before all subsequent descriptors comprising
+        * the list are made available.
+        */
+       virtio_wmb(vq->weak_barriers);
+       vq->packed.vring.desc[head].flags = cpu_to_le16(VRING_DESC_F_INDIRECT |
+                                               vq->packed.avail_used_flags);
+
+       /* We're using some buffers from the free list. */
+       vq->vq.num_free -= 1;
+
+       /* Update free pointer */
+       n = head + 1;
+       if (n >= vq->packed.vring.num) {
+               n = 0;
+               vq->packed.avail_wrap_counter ^= 1;
+               vq->packed.avail_used_flags ^=
+                               1 << VRING_PACKED_DESC_F_AVAIL |
+                               1 << VRING_PACKED_DESC_F_USED;
+       }
+       vq->packed.next_avail_idx = n;
+       vq->free_head = vq->packed.desc_state[id].next;
+
+       /* Store token and indirect buffer state. */
+       vq->packed.desc_state[id].num = 1;
+       vq->packed.desc_state[id].data = data;
+       vq->packed.desc_state[id].indir_desc = desc;
+       vq->packed.desc_state[id].last = id;
+
+       vq->num_added += 1;
+
+       pr_debug("Added buffer head %i to %p\n", head, vq);
+       END_USE(vq);
+
+       return 0;
+
+unmap_release:
+       err_idx = i;
+
+       for (i = 0; i < err_idx; i++)
+               vring_unmap_desc_packed(vq, &desc[i]);
+
+       kfree(desc);
+
+       END_USE(vq);
+       return -EIO;
+}
+
+static inline int virtqueue_add_packed(struct virtqueue *_vq,
+                                      struct scatterlist *sgs[],
+                                      unsigned int total_sg,
+                                      unsigned int out_sgs,
+                                      unsigned int in_sgs,
+                                      void *data,
+                                      void *ctx,
+                                      gfp_t gfp)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       struct vring_packed_desc *desc;
+       struct scatterlist *sg;
+       unsigned int i, n, c, descs_used, err_idx;
+       __le16 uninitialized_var(head_flags), flags;
+       u16 head, id, uninitialized_var(prev), curr, avail_used_flags;
+
+       START_USE(vq);
+
+       BUG_ON(data == NULL);
+       BUG_ON(ctx && vq->indirect);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return -EIO;
+       }
+
+       LAST_ADD_TIME_UPDATE(vq);
+
+       BUG_ON(total_sg == 0);
+
+       if (virtqueue_use_indirect(_vq, total_sg))
+               return virtqueue_add_indirect_packed(vq, sgs, total_sg,
+                               out_sgs, in_sgs, data, gfp);
+
+       head = vq->packed.next_avail_idx;
+       avail_used_flags = vq->packed.avail_used_flags;
+
+       WARN_ON_ONCE(total_sg > vq->packed.vring.num && !vq->indirect);
+
+       desc = vq->packed.vring.desc;
+       i = head;
+       descs_used = total_sg;
+
+       if (unlikely(vq->vq.num_free < descs_used)) {
+               pr_debug("Can't add buf len %i - avail = %i\n",
+                        descs_used, vq->vq.num_free);
+               END_USE(vq);
+               return -ENOSPC;
+       }
+
+       id = vq->free_head;
+       BUG_ON(id == vq->packed.vring.num);
+
+       curr = id;
+       c = 0;
+       for (n = 0; n < out_sgs + in_sgs; n++) {
+               for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+                       dma_addr_t addr = vring_map_one_sg(vq, sg, n < out_sgs ?
+                                       DMA_TO_DEVICE : DMA_FROM_DEVICE);
+                       if (vring_mapping_error(vq, addr))
+                               goto unmap_release;
+
+                       flags = cpu_to_le16(vq->packed.avail_used_flags |
+                                   (++c == total_sg ? 0 : VRING_DESC_F_NEXT) |
+                                   (n < out_sgs ? 0 : VRING_DESC_F_WRITE));
+                       if (i == head)
+                               head_flags = flags;
+                       else
+                               desc[i].flags = flags;
+
+                       desc[i].addr = cpu_to_le64(addr);
+                       desc[i].len = cpu_to_le32(sg->length);
+                       desc[i].id = cpu_to_le16(id);
+
+                       if (unlikely(vq->use_dma_api)) {
+                               vq->packed.desc_extra[curr].addr = addr;
+                               vq->packed.desc_extra[curr].len = sg->length;
+                               vq->packed.desc_extra[curr].flags =
+                                       le16_to_cpu(flags);
+                       }
+                       prev = curr;
+                       curr = vq->packed.desc_state[curr].next;
+
+                       if ((unlikely(++i >= vq->packed.vring.num))) {
+                               i = 0;
+                               vq->packed.avail_used_flags ^=
+                                       1 << VRING_PACKED_DESC_F_AVAIL |
+                                       1 << VRING_PACKED_DESC_F_USED;
+                       }
+               }
+       }
+
+       if (i < head)
+               vq->packed.avail_wrap_counter ^= 1;
+
+       /* We're using some buffers from the free list. */
+       vq->vq.num_free -= descs_used;
+
+       /* Update free pointer */
+       vq->packed.next_avail_idx = i;
+       vq->free_head = curr;
+
+       /* Store token. */
+       vq->packed.desc_state[id].num = descs_used;
+       vq->packed.desc_state[id].data = data;
+       vq->packed.desc_state[id].indir_desc = ctx;
+       vq->packed.desc_state[id].last = prev;
+
+       /*
+        * A driver MUST NOT make the first descriptor in the list
+        * available before all subsequent descriptors comprising
+        * the list are made available.
+        */
+       virtio_wmb(vq->weak_barriers);
+       vq->packed.vring.desc[head].flags = head_flags;
+       vq->num_added += descs_used;
+
+       pr_debug("Added buffer head %i to %p\n", head, vq);
+       END_USE(vq);
+
+       return 0;
+
+unmap_release:
+       err_idx = i;
+       i = head;
+
+       vq->packed.avail_used_flags = avail_used_flags;
+
+       for (n = 0; n < total_sg; n++) {
+               if (i == err_idx)
+                       break;
+               vring_unmap_desc_packed(vq, &desc[i]);
+               i++;
+               if (i >= vq->packed.vring.num)
+                       i = 0;
+       }
+
+       END_USE(vq);
+       return -EIO;
+}
+
+static bool virtqueue_kick_prepare_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 new, old, off_wrap, flags, wrap_counter, event_idx;
+       bool needs_kick;
+       union {
+               struct {
+                       __le16 off_wrap;
+                       __le16 flags;
+               };
+               u32 u32;
+       } snapshot;
+
+       START_USE(vq);
+
+       /*
+        * We need to expose the new flags value before checking notification
+        * suppressions.
+        */
+       virtio_mb(vq->weak_barriers);
+
+       old = vq->packed.next_avail_idx - vq->num_added;
+       new = vq->packed.next_avail_idx;
+       vq->num_added = 0;
+
+       snapshot.u32 = *(u32 *)vq->packed.vring.device;
+       flags = le16_to_cpu(snapshot.flags);
+
+       LAST_ADD_TIME_CHECK(vq);
+       LAST_ADD_TIME_INVALID(vq);
+
+       if (flags != VRING_PACKED_EVENT_FLAG_DESC) {
+               needs_kick = (flags != VRING_PACKED_EVENT_FLAG_DISABLE);
+               goto out;
+       }
+
+       off_wrap = le16_to_cpu(snapshot.off_wrap);
+
+       wrap_counter = off_wrap >> VRING_PACKED_EVENT_F_WRAP_CTR;
+       event_idx = off_wrap & ~(1 << VRING_PACKED_EVENT_F_WRAP_CTR);
+       if (wrap_counter != vq->packed.avail_wrap_counter)
+               event_idx -= vq->packed.vring.num;
+
+       needs_kick = vring_need_event(event_idx, new, old);
+out:
+       END_USE(vq);
+       return needs_kick;
+}
+
+static void detach_buf_packed(struct vring_virtqueue *vq,
+                             unsigned int id, void **ctx)
+{
+       struct vring_desc_state_packed *state = NULL;
+       struct vring_packed_desc *desc;
+       unsigned int i, curr;
+
+       state = &vq->packed.desc_state[id];
+
+       /* Clear data ptr. */
+       state->data = NULL;
+
+       vq->packed.desc_state[state->last].next = vq->free_head;
+       vq->free_head = id;
+       vq->vq.num_free += state->num;
+
+       if (unlikely(vq->use_dma_api)) {
+               curr = id;
+               for (i = 0; i < state->num; i++) {
+                       vring_unmap_state_packed(vq,
+                               &vq->packed.desc_extra[curr]);
+                       curr = vq->packed.desc_state[curr].next;
+               }
+       }
+
+       if (vq->indirect) {
+               u32 len;
+
+               /* Free the indirect table, if any, now that it's unmapped. */
+               desc = state->indir_desc;
+               if (!desc)
+                       return;
+
+               if (vq->use_dma_api) {
+                       len = vq->packed.desc_extra[id].len;
+                       for (i = 0; i < len / sizeof(struct vring_packed_desc);
+                                       i++)
+                               vring_unmap_desc_packed(vq, &desc[i]);
+               }
+               kfree(desc);
+               state->indir_desc = NULL;
+       } else if (ctx) {
+               *ctx = state->indir_desc;
+       }
+}
+
+static inline bool is_used_desc_packed(const struct vring_virtqueue *vq,
+                                      u16 idx, bool used_wrap_counter)
+{
+       bool avail, used;
+       u16 flags;
+
+       flags = le16_to_cpu(vq->packed.vring.desc[idx].flags);
+       avail = !!(flags & (1 << VRING_PACKED_DESC_F_AVAIL));
+       used = !!(flags & (1 << VRING_PACKED_DESC_F_USED));
+
+       return avail == used && used == used_wrap_counter;
+}
+
+static inline bool more_used_packed(const struct vring_virtqueue *vq)
+{
+       return is_used_desc_packed(vq, vq->last_used_idx,
+                       vq->packed.used_wrap_counter);
+}
+
+static void *virtqueue_get_buf_ctx_packed(struct virtqueue *_vq,
+                                         unsigned int *len,
+                                         void **ctx)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 last_used, id;
+       void *ret;
+
+       START_USE(vq);
+
+       if (unlikely(vq->broken)) {
+               END_USE(vq);
+               return NULL;
+       }
+
+       if (!more_used_packed(vq)) {
+               pr_debug("No more buffers in queue\n");
+               END_USE(vq);
+               return NULL;
+       }
+
+       /* Only get used elements after they have been exposed by host. */
+       virtio_rmb(vq->weak_barriers);
+
+       last_used = vq->last_used_idx;
+       id = le16_to_cpu(vq->packed.vring.desc[last_used].id);
+       *len = le32_to_cpu(vq->packed.vring.desc[last_used].len);
+
+       if (unlikely(id >= vq->packed.vring.num)) {
+               BAD_RING(vq, "id %u out of range\n", id);
+               return NULL;
+       }
+       if (unlikely(!vq->packed.desc_state[id].data)) {
+               BAD_RING(vq, "id %u is not a head!\n", id);
+               return NULL;
+       }
+
+       /* detach_buf_packed clears data, so grab it now. */
+       ret = vq->packed.desc_state[id].data;
+       detach_buf_packed(vq, id, ctx);
+
+       vq->last_used_idx += vq->packed.desc_state[id].num;
+       if (unlikely(vq->last_used_idx >= vq->packed.vring.num)) {
+               vq->last_used_idx -= vq->packed.vring.num;
+               vq->packed.used_wrap_counter ^= 1;
+       }
+
+       /*
+        * If we expect an interrupt for the next entry, tell host
+        * by writing event index and flush out the write before
+        * the read in the next get_buf call.
+        */
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DESC)
+               virtio_store_mb(vq->weak_barriers,
+                               &vq->packed.vring.driver->off_wrap,
+                               cpu_to_le16(vq->last_used_idx |
+                                       (vq->packed.used_wrap_counter <<
+                                        VRING_PACKED_EVENT_F_WRAP_CTR)));
+
+       LAST_ADD_TIME_INVALID(vq);
+
+       END_USE(vq);
+       return ret;
+}
+
+static void virtqueue_disable_cb_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       if (vq->packed.event_flags_shadow != VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE;
+               vq->packed.vring.driver->flags =
+                       cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+}
+
+static unsigned virtqueue_enable_cb_prepare_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       START_USE(vq);
+
+       /*
+        * We optimistically turn back on interrupts, then check if there was
+        * more to do.
+        */
+
+       if (vq->event) {
+               vq->packed.vring.driver->off_wrap =
+                       cpu_to_le16(vq->last_used_idx |
+                               (vq->packed.used_wrap_counter <<
+                                VRING_PACKED_EVENT_F_WRAP_CTR));
+               /*
+                * We need to update event offset and event wrap
+                * counter first before updating event flags.
+                */
+               virtio_wmb(vq->weak_barriers);
+       }
+
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = vq->event ?
+                               VRING_PACKED_EVENT_FLAG_DESC :
+                               VRING_PACKED_EVENT_FLAG_ENABLE;
+               vq->packed.vring.driver->flags =
+                               cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       END_USE(vq);
+       return vq->last_used_idx | ((u16)vq->packed.used_wrap_counter <<
+                       VRING_PACKED_EVENT_F_WRAP_CTR);
+}
+
+static bool virtqueue_poll_packed(struct virtqueue *_vq, u16 off_wrap)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       bool wrap_counter;
+       u16 used_idx;
+
+       wrap_counter = off_wrap >> VRING_PACKED_EVENT_F_WRAP_CTR;
+       used_idx = off_wrap & ~(1 << VRING_PACKED_EVENT_F_WRAP_CTR);
+
+       return is_used_desc_packed(vq, used_idx, wrap_counter);
+}
+
+static bool virtqueue_enable_cb_delayed_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       u16 used_idx, wrap_counter;
+       u16 bufs;
+
+       START_USE(vq);
+
+       /*
+        * We optimistically turn back on interrupts, then check if there was
+        * more to do.
+        */
+
+       if (vq->event) {
+               /* TODO: tune this threshold */
+               bufs = (vq->packed.vring.num - vq->vq.num_free) * 3 / 4;
+               wrap_counter = vq->packed.used_wrap_counter;
+
+               used_idx = vq->last_used_idx + bufs;
+               if (used_idx >= vq->packed.vring.num) {
+                       used_idx -= vq->packed.vring.num;
+                       wrap_counter ^= 1;
+               }
+
+               vq->packed.vring.driver->off_wrap = cpu_to_le16(used_idx |
+                       (wrap_counter << VRING_PACKED_EVENT_F_WRAP_CTR));
+
+               /*
+                * We need to update event offset and event wrap
+                * counter first before updating event flags.
+                */
+               virtio_wmb(vq->weak_barriers);
+       } else {
+               used_idx = vq->last_used_idx;
+               wrap_counter = vq->packed.used_wrap_counter;
+       }
+
+       if (vq->packed.event_flags_shadow == VRING_PACKED_EVENT_FLAG_DISABLE) {
+               vq->packed.event_flags_shadow = vq->event ?
+                               VRING_PACKED_EVENT_FLAG_DESC :
+                               VRING_PACKED_EVENT_FLAG_ENABLE;
+               vq->packed.vring.driver->flags =
+                               cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       /*
+        * We need to update event suppression structure first
+        * before re-checking for more used buffers.
+        */
+       virtio_mb(vq->weak_barriers);
+
+       if (is_used_desc_packed(vq, used_idx, wrap_counter)) {
+               END_USE(vq);
+               return false;
+       }
+
+       END_USE(vq);
+       return true;
+}
+
+static void *virtqueue_detach_unused_buf_packed(struct virtqueue *_vq)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+       unsigned int i;
+       void *buf;
+
+       START_USE(vq);
+
+       for (i = 0; i < vq->packed.vring.num; i++) {
+               if (!vq->packed.desc_state[i].data)
+                       continue;
+               /* detach_buf clears data, so grab it now. */
+               buf = vq->packed.desc_state[i].data;
+               detach_buf_packed(vq, i, NULL);
+               END_USE(vq);
+               return buf;
+       }
+       /* That should have freed everything. */
+       BUG_ON(vq->vq.num_free != vq->packed.vring.num);
+
+       END_USE(vq);
+       return NULL;
+}
+
+static struct virtqueue *vring_create_virtqueue_packed(
+       unsigned int index,
+       unsigned int num,
+       unsigned int vring_align,
+       struct virtio_device *vdev,
+       bool weak_barriers,
+       bool may_reduce_num,
+       bool context,
+       bool (*notify)(struct virtqueue *),
+       void (*callback)(struct virtqueue *),
+       const char *name)
+{
+       struct vring_virtqueue *vq;
+       struct vring_packed_desc *ring;
+       struct vring_packed_desc_event *driver, *device;
+       dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr;
+       size_t ring_size_in_bytes, event_size_in_bytes;
+       unsigned int i;
+
+       ring_size_in_bytes = num * sizeof(struct vring_packed_desc);
+
+       ring = vring_alloc_queue(vdev, ring_size_in_bytes,
+                                &ring_dma_addr,
+                                GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!ring)
+               goto err_ring;
+
+       event_size_in_bytes = sizeof(struct vring_packed_desc_event);
+
+       driver = vring_alloc_queue(vdev, event_size_in_bytes,
+                                  &driver_event_dma_addr,
+                                  GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!driver)
+               goto err_driver;
+
+       device = vring_alloc_queue(vdev, event_size_in_bytes,
+                                  &device_event_dma_addr,
+                                  GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
+       if (!device)
+               goto err_device;
+
+       vq = kmalloc(sizeof(*vq), GFP_KERNEL);
+       if (!vq)
+               goto err_vq;
+
+       vq->vq.callback = callback;
+       vq->vq.vdev = vdev;
+       vq->vq.name = name;
+       vq->vq.num_free = num;
+       vq->vq.index = index;
+       vq->we_own_ring = true;
+       vq->notify = notify;
+       vq->weak_barriers = weak_barriers;
+       vq->broken = false;
+       vq->last_used_idx = 0;
+       vq->num_added = 0;
+       vq->packed_ring = true;
+       vq->use_dma_api = vring_use_dma_api(vdev);
+       list_add_tail(&vq->vq.list, &vdev->vqs);
+#ifdef DEBUG
+       vq->in_use = false;
+       vq->last_add_time_valid = false;
+#endif
+
+       vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) &&
+               !context;
+       vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
+
+       vq->packed.ring_dma_addr = ring_dma_addr;
+       vq->packed.driver_event_dma_addr = driver_event_dma_addr;
+       vq->packed.device_event_dma_addr = device_event_dma_addr;
+
+       vq->packed.ring_size_in_bytes = ring_size_in_bytes;
+       vq->packed.event_size_in_bytes = event_size_in_bytes;
+
+       vq->packed.vring.num = num;
+       vq->packed.vring.desc = ring;
+       vq->packed.vring.driver = driver;
+       vq->packed.vring.device = device;
+
+       vq->packed.next_avail_idx = 0;
+       vq->packed.avail_wrap_counter = 1;
+       vq->packed.used_wrap_counter = 1;
+       vq->packed.event_flags_shadow = 0;
+       vq->packed.avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL;
+
+       vq->packed.desc_state = kmalloc_array(num,
+                       sizeof(struct vring_desc_state_packed),
+                       GFP_KERNEL);
+       if (!vq->packed.desc_state)
+               goto err_desc_state;
+
+       memset(vq->packed.desc_state, 0,
+               num * sizeof(struct vring_desc_state_packed));
+
+       /* Put everything in free lists. */
+       vq->free_head = 0;
+       for (i = 0; i < num-1; i++)
+               vq->packed.desc_state[i].next = i + 1;
+
+       vq->packed.desc_extra = kmalloc_array(num,
+                       sizeof(struct vring_desc_extra_packed),
+                       GFP_KERNEL);
+       if (!vq->packed.desc_extra)
+               goto err_desc_extra;
+
+       memset(vq->packed.desc_extra, 0,
+               num * sizeof(struct vring_desc_extra_packed));
+
+       /* No callback?  Tell other side not to bother us. */
+       if (!callback) {
+               vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE;
+               vq->packed.vring.driver->flags =
+                       cpu_to_le16(vq->packed.event_flags_shadow);
+       }
+
+       return &vq->vq;
+
+err_desc_extra:
+       kfree(vq->packed.desc_state);
+err_desc_state:
+       kfree(vq);
+err_vq:
+       vring_free_queue(vdev, event_size_in_bytes, device, ring_dma_addr);
+err_device:
+       vring_free_queue(vdev, event_size_in_bytes, driver, ring_dma_addr);
+err_driver:
+       vring_free_queue(vdev, ring_size_in_bytes, ring, ring_dma_addr);
+err_ring:
+       return NULL;
+}
+
+
+/*
+ * Generic functions and exported symbols.
+ */
+
+static inline int virtqueue_add(struct virtqueue *_vq,
+                               struct scatterlist *sgs[],
+                               unsigned int total_sg,
+                               unsigned int out_sgs,
+                               unsigned int in_sgs,
+                               void *data,
+                               void *ctx,
+                               gfp_t gfp)
+{
+       struct vring_virtqueue *vq = to_vvq(_vq);
+
+       return vq->packed_ring ? virtqueue_add_packed(_vq, sgs, total_sg,
+                                       out_sgs, in_sgs, data, ctx, gfp) :
+                                virtqueue_add_split(_vq, sgs, total_sg,
+                                       out_sgs, in_sgs, data, ctx, gfp);
+}
+
 /**
  * virtqueue_add_sgs - expose buffers to other end
  * @vq: the struct virtqueue we're talking about.
@@ -460,6 +1721,7 @@ int virtqueue_add_sgs(struct virtqueue *_vq,
        /* Count them first. */
        for (i = 0; i < out_sgs + in_sgs; i++) {
                struct scatterlist *sg;
+
                for (sg = sgs[i]; sg; sg = sg_next(sg))
                        total_sg++;
        }
@@ -550,34 +1812,9 @@ EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx);
 bool virtqueue_kick_prepare(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 new, old;
-       bool needs_kick;
-
-       START_USE(vq);
-       /* We need to expose available array entries before checking avail
-        * event. */
-       virtio_mb(vq->weak_barriers);
 
-       old = vq->avail_idx_shadow - vq->num_added;
-       new = vq->avail_idx_shadow;
-       vq->num_added = 0;
-
-#ifdef DEBUG
-       if (vq->last_add_time_valid) {
-               WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),
-                                             vq->last_add_time)) > 100);
-       }
-       vq->last_add_time_valid = false;
-#endif
-
-       if (vq->event) {
-               needs_kick = vring_need_event(virtio16_to_cpu(_vq->vdev, vring_avail_event(&vq->vring)),
-                                             new, old);
-       } else {
-               needs_kick = !(vq->vring.used->flags & cpu_to_virtio16(_vq->vdev, VRING_USED_F_NO_NOTIFY));
-       }
-       END_USE(vq);
-       return needs_kick;
+       return vq->packed_ring ? virtqueue_kick_prepare_packed(_vq) :
+                                virtqueue_kick_prepare_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_kick_prepare);
 
@@ -625,60 +1862,6 @@ bool virtqueue_kick(struct virtqueue *vq)
 }
 EXPORT_SYMBOL_GPL(virtqueue_kick);
 
-static void detach_buf(struct vring_virtqueue *vq, unsigned int head,
-                      void **ctx)
-{
-       unsigned int i, j;
-       __virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
-
-       /* Clear data ptr. */
-       vq->desc_state[head].data = NULL;
-
-       /* Put back on free list: unmap first-level descriptors and find end */
-       i = head;
-
-       while (vq->vring.desc[i].flags & nextflag) {
-               vring_unmap_one(vq, &vq->vring.desc[i]);
-               i = virtio16_to_cpu(vq->vq.vdev, vq->vring.desc[i].next);
-               vq->vq.num_free++;
-       }
-
-       vring_unmap_one(vq, &vq->vring.desc[i]);
-       vq->vring.desc[i].next = cpu_to_virtio16(vq->vq.vdev, vq->free_head);
-       vq->free_head = head;
-
-       /* Plus final descriptor */
-       vq->vq.num_free++;
-
-       if (vq->indirect) {
-               struct vring_desc *indir_desc = vq->desc_state[head].indir_desc;
-               u32 len;
-
-               /* Free the indirect table, if any, now that it's unmapped. */
-               if (!indir_desc)
-                       return;
-
-               len = virtio32_to_cpu(vq->vq.vdev, vq->vring.desc[head].len);
-
-               BUG_ON(!(vq->vring.desc[head].flags &
-                        cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_INDIRECT)));
-               BUG_ON(len == 0 || len % sizeof(struct vring_desc));
-
-               for (j = 0; j < len / sizeof(struct vring_desc); j++)
-                       vring_unmap_one(vq, &indir_desc[j]);
-
-               kfree(indir_desc);
-               vq->desc_state[head].indir_desc = NULL;
-       } else if (ctx) {
-               *ctx = vq->desc_state[head].indir_desc;
-       }
-}
-
-static inline bool more_used(const struct vring_virtqueue *vq)
-{
-       return vq->last_used_idx != virtio16_to_cpu(vq->vq.vdev, vq->vring.used->idx);
-}
-
 /**
  * virtqueue_get_buf - get the next used buffer
  * @vq: the struct virtqueue we're talking about.
@@ -699,57 +1882,9 @@ void *virtqueue_get_buf_ctx(struct virtqueue *_vq, unsigned int *len,
                            void **ctx)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       void *ret;
-       unsigned int i;
-       u16 last_used;
-
-       START_USE(vq);
-
-       if (unlikely(vq->broken)) {
-               END_USE(vq);
-               return NULL;
-       }
-
-       if (!more_used(vq)) {
-               pr_debug("No more buffers in queue\n");
-               END_USE(vq);
-               return NULL;
-       }
-
-       /* Only get used array entries after they have been exposed by host. */
-       virtio_rmb(vq->weak_barriers);
-
-       last_used = (vq->last_used_idx & (vq->vring.num - 1));
-       i = virtio32_to_cpu(_vq->vdev, vq->vring.used->ring[last_used].id);
-       *len = virtio32_to_cpu(_vq->vdev, vq->vring.used->ring[last_used].len);
-
-       if (unlikely(i >= vq->vring.num)) {
-               BAD_RING(vq, "id %u out of range\n", i);
-               return NULL;
-       }
-       if (unlikely(!vq->desc_state[i].data)) {
-               BAD_RING(vq, "id %u is not a head!\n", i);
-               return NULL;
-       }
-
-       /* detach_buf clears data, so grab it now. */
-       ret = vq->desc_state[i].data;
-       detach_buf(vq, i, ctx);
-       vq->last_used_idx++;
-       /* If we expect an interrupt for the next entry, tell host
-        * by writing event index and flush out the write before
-        * the read in the next get_buf call. */
-       if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT))
-               virtio_store_mb(vq->weak_barriers,
-                               &vring_used_event(&vq->vring),
-                               cpu_to_virtio16(_vq->vdev, vq->last_used_idx));
-
-#ifdef DEBUG
-       vq->last_add_time_valid = false;
-#endif
 
-       END_USE(vq);
-       return ret;
+       return vq->packed_ring ? virtqueue_get_buf_ctx_packed(_vq, len, ctx) :
+                                virtqueue_get_buf_ctx_split(_vq, len, ctx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_buf_ctx);
 
@@ -771,12 +1906,10 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
 
-       if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
-               vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-
+       if (vq->packed_ring)
+               virtqueue_disable_cb_packed(_vq);
+       else
+               virtqueue_disable_cb_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
 
@@ -795,23 +1928,9 @@ EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
 unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 last_used_idx;
-
-       START_USE(vq);
 
-       /* We optimistically turn back on interrupts, then check if there was
-        * more to do. */
-       /* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
-        * either clear the flags bit or point the event index at the next
-        * entry. Always do both to keep code simple. */
-       if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
-               vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-       vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
-       END_USE(vq);
-       return last_used_idx;
+       return vq->packed_ring ? virtqueue_enable_cb_prepare_packed(_vq) :
+                                virtqueue_enable_cb_prepare_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb_prepare);
 
@@ -829,7 +1948,8 @@ bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx)
        struct vring_virtqueue *vq = to_vvq(_vq);
 
        virtio_mb(vq->weak_barriers);
-       return (u16)last_used_idx != virtio16_to_cpu(_vq->vdev, vq->vring.used->idx);
+       return vq->packed_ring ? virtqueue_poll_packed(_vq, last_used_idx) :
+                                virtqueue_poll_split(_vq, last_used_idx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_poll);
 
@@ -847,6 +1967,7 @@ EXPORT_SYMBOL_GPL(virtqueue_poll);
 bool virtqueue_enable_cb(struct virtqueue *_vq)
 {
        unsigned last_used_idx = virtqueue_enable_cb_prepare(_vq);
+
        return !virtqueue_poll(_vq, last_used_idx);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
@@ -867,34 +1988,9 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb);
 bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       u16 bufs;
-
-       START_USE(vq);
-
-       /* We optimistically turn back on interrupts, then check if there was
-        * more to do. */
-       /* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
-        * either clear the flags bit or point the event index at the next
-        * entry. Always update the event index to keep code simple. */
-       if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
-               vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
-               if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
-       }
-       /* TODO: tune this threshold */
-       bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
-
-       virtio_store_mb(vq->weak_barriers,
-                       &vring_used_event(&vq->vring),
-                       cpu_to_virtio16(_vq->vdev, vq->last_used_idx + bufs));
-
-       if (unlikely((u16)(virtio16_to_cpu(_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) {
-               END_USE(vq);
-               return false;
-       }
 
-       END_USE(vq);
-       return true;
+       return vq->packed_ring ? virtqueue_enable_cb_delayed_packed(_vq) :
+                                virtqueue_enable_cb_delayed_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);
 
@@ -909,30 +2005,17 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed);
 void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-       unsigned int i;
-       void *buf;
-
-       START_USE(vq);
-
-       for (i = 0; i < vq->vring.num; i++) {
-               if (!vq->desc_state[i].data)
-                       continue;
-               /* detach_buf clears data, so grab it now. */
-               buf = vq->desc_state[i].data;
-               detach_buf(vq, i, NULL);
-               vq->avail_idx_shadow--;
-               vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
-               END_USE(vq);
-               return buf;
-       }
-       /* That should have freed everything. */
-       BUG_ON(vq->vq.num_free != vq->vring.num);
 
-       END_USE(vq);
-       return NULL;
+       return vq->packed_ring ? virtqueue_detach_unused_buf_packed(_vq) :
+                                virtqueue_detach_unused_buf_split(_vq);
 }
 EXPORT_SYMBOL_GPL(virtqueue_detach_unused_buf);
 
+static inline bool more_used(const struct vring_virtqueue *vq)
+{
+       return vq->packed_ring ? more_used_packed(vq) : more_used_split(vq);
+}
+
 irqreturn_t vring_interrupt(int irq, void *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
@@ -953,6 +2036,7 @@ irqreturn_t vring_interrupt(int irq, void *_vq)
 }
 EXPORT_SYMBOL_GPL(vring_interrupt);
 
+/* Only available for split ring */
 struct virtqueue *__vring_new_virtqueue(unsigned int index,
                                        struct vring vring,
                                        struct virtio_device *vdev,
@@ -965,27 +2049,26 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,
        unsigned int i;
        struct vring_virtqueue *vq;
 
-       vq = kmalloc(sizeof(*vq) + vring.num * sizeof(struct vring_desc_state),
-                    GFP_KERNEL);
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return NULL;
+
+       vq = kmalloc(sizeof(*vq), GFP_KERNEL);
        if (!vq)
                return NULL;
 
-       vq->vring = vring;
+       vq->packed_ring = false;
        vq->vq.callback = callback;
        vq->vq.vdev = vdev;
        vq->vq.name = name;
        vq->vq.num_free = vring.num;
        vq->vq.index = index;
        vq->we_own_ring = false;
-       vq->queue_dma_addr = 0;
-       vq->queue_size_in_bytes = 0;
        vq->notify = notify;
        vq->weak_barriers = weak_barriers;
        vq->broken = false;
        vq->last_used_idx = 0;
-       vq->avail_flags_shadow = 0;
-       vq->avail_idx_shadow = 0;
        vq->num_added = 0;
+       vq->use_dma_api = vring_use_dma_api(vdev);
        list_add_tail(&vq->vq.list, &vdev->vqs);
 #ifdef DEBUG
        vq->in_use = false;
@@ -996,65 +2079,39 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index,
                !context;
        vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
 
+       vq->split.queue_dma_addr = 0;
+       vq->split.queue_size_in_bytes = 0;
+
+       vq->split.vring = vring;
+       vq->split.avail_flags_shadow = 0;
+       vq->split.avail_idx_shadow = 0;
+
        /* No callback?  Tell other side not to bother us. */
        if (!callback) {
-               vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+               vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
                if (!vq->event)
-                       vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+                       vq->split.vring.avail->flags = cpu_to_virtio16(vdev,
+                                       vq->split.avail_flags_shadow);
+       }
+
+       vq->split.desc_state = kmalloc_array(vring.num,
+                       sizeof(struct vring_desc_state_split), GFP_KERNEL);
+       if (!vq->split.desc_state) {
+               kfree(vq);
+               return NULL;
        }
 
        /* Put everything in free lists. */
        vq->free_head = 0;
        for (i = 0; i < vring.num-1; i++)
-               vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
-       memset(vq->desc_state, 0, vring.num * sizeof(struct vring_desc_state));
+               vq->split.vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
+       memset(vq->split.desc_state, 0, vring.num *
+                       sizeof(struct vring_desc_state_split));
 
        return &vq->vq;
 }
 EXPORT_SYMBOL_GPL(__vring_new_virtqueue);
 
-static void *vring_alloc_queue(struct virtio_device *vdev, size_t size,
-                             dma_addr_t *dma_handle, gfp_t flag)
-{
-       if (vring_use_dma_api(vdev)) {
-               return dma_alloc_coherent(vdev->dev.parent, size,
-                                         dma_handle, flag);
-       } else {
-               void *queue = alloc_pages_exact(PAGE_ALIGN(size), flag);
-               if (queue) {
-                       phys_addr_t phys_addr = virt_to_phys(queue);
-                       *dma_handle = (dma_addr_t)phys_addr;
-
-                       /*
-                        * Sanity check: make sure we dind't truncate
-                        * the address.  The only arches I can find that
-                        * have 64-bit phys_addr_t but 32-bit dma_addr_t
-                        * are certain non-highmem MIPS and x86
-                        * configurations, but these configurations
-                        * should never allocate physical pages above 32
-                        * bits, so this is fine.  Just in case, throw a
-                        * warning and abort if we end up with an
-                        * unrepresentable address.
-                        */
-                       if (WARN_ON_ONCE(*dma_handle != phys_addr)) {
-                               free_pages_exact(queue, PAGE_ALIGN(size));
-                               return NULL;
-                       }
-               }
-               return queue;
-       }
-}
-
-static void vring_free_queue(struct virtio_device *vdev, size_t size,
-                            void *queue, dma_addr_t dma_handle)
-{
-       if (vring_use_dma_api(vdev)) {
-               dma_free_coherent(vdev->dev.parent, size, queue, dma_handle);
-       } else {
-               free_pages_exact(queue, PAGE_ALIGN(size));
-       }
-}
-
 struct virtqueue *vring_create_virtqueue(
        unsigned int index,
        unsigned int num,
@@ -1067,57 +2124,19 @@ struct virtqueue *vring_create_virtqueue(
        void (*callback)(struct virtqueue *),
        const char *name)
 {
-       struct virtqueue *vq;
-       void *queue = NULL;
-       dma_addr_t dma_addr;
-       size_t queue_size_in_bytes;
-       struct vring vring;
-
-       /* We assume num is a power of 2. */
-       if (num & (num - 1)) {
-               dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num);
-               return NULL;
-       }
-
-       /* TODO: allocate each queue chunk individually */
-       for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
-               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
-                                         &dma_addr,
-                                         GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO);
-               if (queue)
-                       break;
-       }
 
-       if (!num)
-               return NULL;
-
-       if (!queue) {
-               /* Try to get a single page. You are my only hope! */
-               queue = vring_alloc_queue(vdev, vring_size(num, vring_align),
-                                         &dma_addr, GFP_KERNEL|__GFP_ZERO);
-       }
-       if (!queue)
-               return NULL;
-
-       queue_size_in_bytes = vring_size(num, vring_align);
-       vring_init(&vring, num, queue, vring_align);
-
-       vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
-                                  notify, callback, name);
-       if (!vq) {
-               vring_free_queue(vdev, queue_size_in_bytes, queue,
-                                dma_addr);
-               return NULL;
-       }
-
-       to_vvq(vq)->queue_dma_addr = dma_addr;
-       to_vvq(vq)->queue_size_in_bytes = queue_size_in_bytes;
-       to_vvq(vq)->we_own_ring = true;
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return vring_create_virtqueue_packed(index, num, vring_align,
+                               vdev, weak_barriers, may_reduce_num,
+                               context, notify, callback, name);
 
-       return vq;
+       return vring_create_virtqueue_split(index, num, vring_align,
+                       vdev, weak_barriers, may_reduce_num,
+                       context, notify, callback, name);
 }
 EXPORT_SYMBOL_GPL(vring_create_virtqueue);
 
+/* Only available for split ring */
 struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      unsigned int num,
                                      unsigned int vring_align,
@@ -1130,6 +2149,10 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
                                      const char *name)
 {
        struct vring vring;
+
+       if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
+               return NULL;
+
        vring_init(&vring, num, pages, vring_align);
        return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context,
                                     notify, callback, name);
@@ -1141,8 +2164,32 @@ void vring_del_virtqueue(struct virtqueue *_vq)
        struct vring_virtqueue *vq = to_vvq(_vq);
 
        if (vq->we_own_ring) {
-               vring_free_queue(vq->vq.vdev, vq->queue_size_in_bytes,
-                                vq->vring.desc, vq->queue_dma_addr);
+               if (vq->packed_ring) {
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.ring_size_in_bytes,
+                                        vq->packed.vring.desc,
+                                        vq->packed.ring_dma_addr);
+
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.event_size_in_bytes,
+                                        vq->packed.vring.driver,
+                                        vq->packed.driver_event_dma_addr);
+
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->packed.event_size_in_bytes,
+                                        vq->packed.vring.device,
+                                        vq->packed.device_event_dma_addr);
+
+                       kfree(vq->packed.desc_state);
+                       kfree(vq->packed.desc_extra);
+               } else {
+                       vring_free_queue(vq->vq.vdev,
+                                        vq->split.queue_size_in_bytes,
+                                        vq->split.vring.desc,
+                                        vq->split.queue_dma_addr);
+
+                       kfree(vq->split.desc_state);
+               }
        }
        list_del(&_vq->list);
        kfree(vq);
@@ -1164,6 +2211,8 @@ void vring_transport_features(struct virtio_device *vdev)
                        break;
                case VIRTIO_F_IOMMU_PLATFORM:
                        break;
+               case VIRTIO_F_RING_PACKED:
+                       break;
                default:
                        /* We don't understand this bit. */
                        __virtio_clear_bit(vdev, i);
@@ -1184,7 +2233,7 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq)
 
        struct vring_virtqueue *vq = to_vvq(_vq);
 
-       return vq->vring.num;
+       return vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_vring_size);
 
@@ -1217,7 +2266,10 @@ dma_addr_t virtqueue_get_desc_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr;
+       if (vq->packed_ring)
+               return vq->packed.ring_dma_addr;
+
+       return vq->split.queue_dma_addr;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_desc_addr);
 
@@ -1227,8 +2279,11 @@ dma_addr_t virtqueue_get_avail_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr +
-               ((char *)vq->vring.avail - (char *)vq->vring.desc);
+       if (vq->packed_ring)
+               return vq->packed.driver_event_dma_addr;
+
+       return vq->split.queue_dma_addr +
+               ((char *)vq->split.vring.avail - (char *)vq->split.vring.desc);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_avail_addr);
 
@@ -1238,14 +2293,18 @@ dma_addr_t virtqueue_get_used_addr(struct virtqueue *_vq)
 
        BUG_ON(!vq->we_own_ring);
 
-       return vq->queue_dma_addr +
-               ((char *)vq->vring.used - (char *)vq->vring.desc);
+       if (vq->packed_ring)
+               return vq->packed.device_event_dma_addr;
+
+       return vq->split.queue_dma_addr +
+               ((char *)vq->split.vring.used - (char *)vq->split.vring.desc);
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_used_addr);
 
+/* Only available for split ring */
 const struct vring *virtqueue_get_vring(struct virtqueue *vq)
 {
-       return &to_vvq(vq)->vring;
+       return &to_vvq(vq)->split.vring;
 }
 EXPORT_SYMBOL_GPL(virtqueue_get_vring);
 
index b248805..7605b59 100644 (file)
@@ -171,7 +171,7 @@ struct virtchnl_msg {
 
 VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
 
-/* Message descriptions and data structures.*/
+/* Message descriptions and data structures. */
 
 /* VIRTCHNL_OP_VERSION
  * VF posts its version number to the PF. PF responds with its version number
@@ -342,6 +342,8 @@ struct virtchnl_vsi_queue_config_info {
        struct virtchnl_queue_pair_info qpair[1];
 };
 
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
+
 /* VIRTCHNL_OP_REQUEST_QUEUES
  * VF sends this message to request the PF to allocate additional queues to
  * this VF.  Each VF gets a guaranteed number of queues on init but asking for
@@ -357,8 +359,6 @@ struct virtchnl_vf_res_request {
        u16 num_queue_pairs;
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
-
 /* VIRTCHNL_OP_CONFIG_IRQ_MAP
  * VF uses this message to map vectors to queues.
  * The rxq_map and txq_map fields are bitmaps used to indicate which queues
@@ -819,8 +819,8 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
                if (msglen >= valid_len) {
                        struct virtchnl_tc_info *vti =
                                (struct virtchnl_tc_info *)msg;
-                       valid_len += vti->num_tc *
-                               sizeof(struct virtchnl_channel_info);
+                       valid_len += (vti->num_tc - 1) *
+                                    sizeof(struct virtchnl_channel_info);
                        if (vti->num_tc == 0)
                                err_msg_format = true;
                }
index 33014ae..e82b703 100644 (file)
@@ -268,15 +268,18 @@ struct bpf_prog_offload_ops {
        int (*insn_hook)(struct bpf_verifier_env *env,
                         int insn_idx, int prev_insn_idx);
        int (*finalize)(struct bpf_verifier_env *env);
+       int (*prepare)(struct bpf_prog *prog);
+       int (*translate)(struct bpf_prog *prog);
+       void (*destroy)(struct bpf_prog *prog);
 };
 
 struct bpf_prog_offload {
        struct bpf_prog         *prog;
        struct net_device       *netdev;
+       struct bpf_offload_dev  *offdev;
        void                    *dev_priv;
        struct list_head        offloads;
        bool                    dev_state;
-       const struct bpf_prog_offload_ops *dev_ops;
        void                    *jited_image;
        u32                     jited_len;
 };
@@ -293,9 +296,11 @@ struct bpf_prog_aux {
        atomic_t refcnt;
        u32 used_map_cnt;
        u32 max_ctx_offset;
+       u32 max_pkt_offset;
        u32 stack_depth;
        u32 id;
-       u32 func_cnt;
+       u32 func_cnt; /* used by non-func prog as the number of func progs */
+       u32 func_idx; /* 0 for non-func prog, the index in func array for func prog */
        bool offload_requested;
        struct bpf_prog **func;
        void *jit_data; /* JIT specific data. arch dependent */
@@ -312,6 +317,9 @@ struct bpf_prog_aux {
        void *security;
 #endif
        struct bpf_prog_offload *offload;
+       struct btf *btf;
+       struct bpf_func_info *func_info;
+       u32 func_info_cnt;
        union {
                struct work_struct work;
                struct rcu_head rcu;
@@ -523,7 +531,8 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
 }
 
 /* verify correctness of eBPF program */
-int bpf_check(struct bpf_prog **fp, union bpf_attr *attr);
+int bpf_check(struct bpf_prog **fp, union bpf_attr *attr,
+             union bpf_attr __user *uattr);
 void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
 
 /* Map specifics */
@@ -691,7 +700,8 @@ int bpf_map_offload_get_next_key(struct bpf_map *map,
 
 bool bpf_offload_prog_map_match(struct bpf_prog *prog, struct bpf_map *map);
 
-struct bpf_offload_dev *bpf_offload_dev_create(void);
+struct bpf_offload_dev *
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops);
 void bpf_offload_dev_destroy(struct bpf_offload_dev *offdev);
 int bpf_offload_dev_netdev_register(struct bpf_offload_dev *offdev,
                                    struct net_device *netdev);
index d93e897..11f5df1 100644 (file)
@@ -245,7 +245,7 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env)
        return cur_func(env)->regs;
 }
 
-int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env);
+int bpf_prog_offload_verifier_prep(struct bpf_prog *prog);
 int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
                                 int insn_idx, int prev_insn_idx);
 int bpf_prog_offload_finalize(struct bpf_verifier_env *env);
index 949e9af..9cd00a3 100644 (file)
@@ -28,6 +28,7 @@
 #define PHY_ID_BCM89610                        0x03625cd0
 
 #define PHY_ID_BCM7250                 0xae025280
+#define PHY_ID_BCM7255                 0xae025120
 #define PHY_ID_BCM7260                 0xae025190
 #define PHY_ID_BCM7268                 0xae025090
 #define PHY_ID_BCM7271                 0xae0253b0
index e076c46..8c2199b 100644 (file)
@@ -47,4 +47,20 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
 int btf_get_fd_by_id(u32 id);
 u32 btf_id(const struct btf *btf);
 
+#ifdef CONFIG_BPF_SYSCALL
+const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id);
+const char *btf_name_by_offset(const struct btf *btf, u32 offset);
+#else
+static inline const struct btf_type *btf_type_by_id(const struct btf *btf,
+                                                   u32 type_id)
+{
+       return NULL;
+}
+static inline const char *btf_name_by_offset(const struct btf *btf,
+                                            u32 offset)
+{
+       return NULL;
+}
+#endif
+
 #endif
index cf68ca4..3d656f5 100644 (file)
 
 #include <linux/types.h>
 
+#define CORDIC_ANGLE_GEN       39797
+#define CORDIC_PRECISION_SHIFT 16
+#define CORDIC_NUM_ITER        (CORDIC_PRECISION_SHIFT + 2)
+
+#define CORDIC_FIXED(X)        ((s32)((X) << CORDIC_PRECISION_SHIFT))
+#define CORDIC_FLOAT(X)        (((X) >= 0) \
+               ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
+               : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
+
 /**
  * struct cordic_iq - i/q coordinate.
  *
index 572e11b..2c0af7b 100644 (file)
@@ -32,6 +32,7 @@
 struct device;
 int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr);
 unsigned char *arch_get_platform_mac_address(void);
+int nvmem_get_mac_address(struct device *dev, void *addrbuf);
 u32 eth_get_headlen(void *data, unsigned int max_len);
 __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
 extern const struct header_ops eth_header_ops;
index 795ff0b..5a26a7c 100644 (file)
@@ -675,24 +675,10 @@ static inline u32 bpf_ctx_off_adjust_machine(u32 size)
        return size;
 }
 
-static inline bool bpf_ctx_narrow_align_ok(u32 off, u32 size_access,
-                                          u32 size_default)
-{
-       size_default = bpf_ctx_off_adjust_machine(size_default);
-       size_access  = bpf_ctx_off_adjust_machine(size_access);
-
-#ifdef __LITTLE_ENDIAN
-       return (off & (size_default - 1)) == 0;
-#else
-       return (off & (size_default - 1)) + size_access == size_default;
-#endif
-}
-
 static inline bool
 bpf_ctx_narrow_access_ok(u32 off, u32 size, u32 size_default)
 {
-       return bpf_ctx_narrow_align_ok(off, size, size_default) &&
-              size <= size_default && (size & (size - 1)) == 0;
+       return size <= size_default && (size & (size - 1)) == 0;
 }
 
 #define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
index c20c7e1..627b788 100644 (file)
@@ -119,6 +119,8 @@ static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
 struct net_device *br_fdb_find_port(const struct net_device *br_dev,
                                    const unsigned char *addr,
                                    __u16 vid);
+void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
+bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
 #else
 static inline struct net_device *
 br_fdb_find_port(const struct net_device *br_dev,
@@ -127,6 +129,16 @@ br_fdb_find_port(const struct net_device *br_dev,
 {
        return NULL;
 }
+
+static inline void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
+{
+}
+
+static inline bool
+br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+{
+       return false;
+}
 #endif
 
 #endif
index 83ea4df..4cca4da 100644 (file)
@@ -65,8 +65,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
 
 #define VLAN_PRIO_MASK         0xe000 /* Priority Code Point */
 #define VLAN_PRIO_SHIFT                13
-#define VLAN_CFI_MASK          0x1000 /* Canonical Format Indicator */
-#define VLAN_TAG_PRESENT       VLAN_CFI_MASK
+#define VLAN_CFI_MASK          0x1000 /* Canonical Format Indicator / Drop Eligible Indicator */
 #define VLAN_VID_MASK          0x0fff /* VLAN Identifier */
 #define VLAN_N_VID             4096
 
@@ -78,10 +77,11 @@ static inline bool is_vlan_dev(const struct net_device *dev)
         return dev->priv_flags & IFF_802_1Q_VLAN;
 }
 
-#define skb_vlan_tag_present(__skb)    ((__skb)->vlan_tci & VLAN_TAG_PRESENT)
-#define skb_vlan_tag_get(__skb)                ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT)
+#define skb_vlan_tag_present(__skb)    ((__skb)->vlan_present)
+#define skb_vlan_tag_get(__skb)                ((__skb)->vlan_tci)
 #define skb_vlan_tag_get_id(__skb)     ((__skb)->vlan_tci & VLAN_VID_MASK)
-#define skb_vlan_tag_get_prio(__skb)   ((__skb)->vlan_tci & VLAN_PRIO_MASK)
+#define skb_vlan_tag_get_cfi(__skb)    (!!((__skb)->vlan_tci & VLAN_CFI_MASK))
+#define skb_vlan_tag_get_prio(__skb)   (((__skb)->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT)
 
 static inline int vlan_get_rx_ctag_filter_info(struct net_device *dev)
 {
@@ -133,6 +133,9 @@ struct vlan_pcpu_stats {
 
 extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev,
                                               __be16 vlan_proto, u16 vlan_id);
+extern int vlan_for_each(struct net_device *dev,
+                        int (*action)(struct net_device *dev, int vid,
+                                      void *arg), void *arg);
 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 __be16 vlan_dev_vlan_proto(const struct net_device *dev);
@@ -236,6 +239,14 @@ __vlan_find_dev_deep_rcu(struct net_device *real_dev,
        return NULL;
 }
 
+static inline int
+vlan_for_each(struct net_device *dev,
+             int (*action)(struct net_device *dev, int vid, void *arg),
+             void *arg)
+{
+       return 0;
+}
+
 static inline struct net_device *vlan_dev_real_dev(const struct net_device *dev)
 {
        BUG();
@@ -461,6 +472,31 @@ static inline struct sk_buff *vlan_insert_tag_set_proto(struct sk_buff *skb,
        return skb;
 }
 
+/**
+ * __vlan_hwaccel_clear_tag - clear hardware accelerated VLAN info
+ * @skb: skbuff to clear
+ *
+ * Clears the VLAN information from @skb
+ */
+static inline void __vlan_hwaccel_clear_tag(struct sk_buff *skb)
+{
+       skb->vlan_present = 0;
+}
+
+/**
+ * __vlan_hwaccel_copy_tag - copy hardware accelerated VLAN info from another skb
+ * @dst: skbuff to copy to
+ * @src: skbuff to copy from
+ *
+ * Copies VLAN information from @src to @dst (for branchless code)
+ */
+static inline void __vlan_hwaccel_copy_tag(struct sk_buff *dst, const struct sk_buff *src)
+{
+       dst->vlan_present = src->vlan_present;
+       dst->vlan_proto = src->vlan_proto;
+       dst->vlan_tci = src->vlan_tci;
+}
+
 /*
  * __vlan_hwaccel_push_inside - pushes vlan tag to the payload
  * @skb: skbuff to tag
@@ -475,7 +511,7 @@ static inline struct sk_buff *__vlan_hwaccel_push_inside(struct sk_buff *skb)
        skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
                                        skb_vlan_tag_get(skb));
        if (likely(skb))
-               skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(skb);
        return skb;
 }
 
@@ -491,7 +527,8 @@ static inline void __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;
+       skb->vlan_tci = vlan_tci;
+       skb->vlan_present = 1;
 }
 
 /**
@@ -531,8 +568,6 @@ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb,
        }
 }
 
-#define HAVE_VLAN_GET_TAG
-
 /**
  * vlan_get_tag - get the VLAN ID from the skb
  * @skb: skbuff to query
index 22443d7..a99c588 100644 (file)
@@ -57,6 +57,15 @@ static inline void linkmode_clear_bit(int nr, volatile unsigned long *addr)
        __clear_bit(nr, addr);
 }
 
+static inline void linkmode_mod_bit(int nr, volatile unsigned long *addr,
+                                   int set)
+{
+       if (set)
+               linkmode_set_bit(nr, addr);
+       else
+               linkmode_clear_bit(nr, addr);
+}
+
 static inline void linkmode_change_bit(int nr, volatile unsigned long *addr)
 {
        __change_bit(nr, addr);
index 2da85b0..6fee8b1 100644 (file)
@@ -209,7 +209,7 @@ static inline u32 ethtool_adv_to_mii_ctrl1000_t(u32 ethadv)
 
 /**
  * linkmode_adv_to_mii_ctrl1000_t
- * advertising: the linkmode advertisement settings
+ * @advertising: the linkmode advertisement settings
  *
  * A small helper function that translates linkmode advertisement
  * settings to phy autonegotiation advertisements for the
@@ -288,6 +288,25 @@ static inline u32 mii_stat1000_to_ethtool_lpa_t(u32 lpa)
 }
 
 /**
+ * mii_stat1000_mod_linkmode_lpa_t
+ * @advertising: target the linkmode advertisement settings
+ * @adv: value of the MII_STAT1000 register
+ *
+ * A small helper function that translates MII_STAT1000 bits, when in
+ * 1000Base-T mode, to linkmode advertisement settings. Other bits in
+ * advertising are not changes.
+ */
+static inline void mii_stat1000_mod_linkmode_lpa_t(unsigned long *advertising,
+                                                  u32 lpa)
+{
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+                        advertising, lpa & LPA_1000HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+                        advertising, lpa & LPA_1000FULL);
+}
+
+/**
  * ethtool_adv_to_mii_adv_x
  * @ethadv: the ethtool advertisement settings
  *
@@ -354,50 +373,104 @@ static inline u32 mii_lpa_to_ethtool_lpa_x(u32 lpa)
 }
 
 /**
+ * mii_adv_mod_linkmode_adv_t
+ * @advertising:pointer to destination link mode.
+ * @adv: value of the MII_ADVERTISE register
+ *
+ * A small helper function that translates MII_ADVERTISE bits to
+ * linkmode advertisement settings. Leaves other bits unchanged.
+ */
+static inline void mii_adv_mod_linkmode_adv_t(unsigned long *advertising,
+                                             u32 adv)
+{
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+                        advertising, adv & ADVERTISE_10HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+                        advertising, adv & ADVERTISE_10FULL);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+                        advertising, adv & ADVERTISE_100HALF);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+                        advertising, adv & ADVERTISE_100FULL);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising,
+                        adv & ADVERTISE_PAUSE_CAP);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                        advertising, adv & ADVERTISE_PAUSE_ASYM);
+}
+
+/**
  * mii_adv_to_linkmode_adv_t
  * @advertising:pointer to destination link mode.
  * @adv: value of the MII_ADVERTISE register
  *
  * A small helper function that translates MII_ADVERTISE bits
- * to linkmode advertisement settings.
+ * to linkmode advertisement settings. Clears the old value
+ * of advertising.
  */
 static inline void mii_adv_to_linkmode_adv_t(unsigned long *advertising,
                                             u32 adv)
 {
        linkmode_zero(advertising);
 
-       if (adv & ADVERTISE_10HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
-                                advertising);
-       if (adv & ADVERTISE_10FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
-                                advertising);
-       if (adv & ADVERTISE_100HALF)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                advertising);
-       if (adv & ADVERTISE_100FULL)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                advertising);
-       if (adv & ADVERTISE_PAUSE_CAP)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising);
-       if (adv & ADVERTISE_PAUSE_ASYM)
-               linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising);
+       mii_adv_mod_linkmode_adv_t(advertising, adv);
+}
+
+/**
+ * mii_lpa_to_linkmode_lpa_t
+ * @adv: value of the MII_LPA register
+ *
+ * A small helper function that translates MII_LPA bits, when in
+ * 1000Base-T mode, to linkmode LP advertisement settings. Clears the
+ * old value of advertising
+ */
+static inline void mii_lpa_to_linkmode_lpa_t(unsigned long *lp_advertising,
+                                            u32 lpa)
+{
+       mii_adv_to_linkmode_adv_t(lp_advertising, lpa);
+
+       if (lpa & LPA_LPACK)
+               linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                lp_advertising);
+
+}
+
+/**
+ * mii_lpa_mod_linkmode_lpa_t
+ * @adv: value of the MII_LPA register
+ *
+ * A small helper function that translates MII_LPA bits, when in
+ * 1000Base-T mode, to linkmode LP advertisement settings. Leaves
+ * other bits unchanged.
+ */
+static inline void mii_lpa_mod_linkmode_lpa_t(unsigned long *lp_advertising,
+                                             u32 lpa)
+{
+       mii_adv_mod_linkmode_adv_t(lp_advertising, lpa);
+
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                        lp_advertising, lpa & LPA_LPACK);
 }
 
 /**
- * ethtool_adv_to_lcl_adv_t
- * @advertising:pointer to ethtool advertising
+ * linkmode_adv_to_lcl_adv_t
+ * @advertising:pointer to linkmode advertising
  *
- * A small helper function that translates ethtool advertising to LVL
+ * A small helper function that translates linkmode advertising to LVL
  * pause capabilities.
  */
-static inline u32 ethtool_adv_to_lcl_adv_t(u32 advertising)
+static inline u32 linkmode_adv_to_lcl_adv_t(unsigned long *advertising)
 {
        u32 lcl_adv = 0;
 
-       if (advertising & ADVERTISED_Pause)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                             advertising))
                lcl_adv |= ADVERTISE_PAUSE_CAP;
-       if (advertising & ADVERTISED_Asym_Pause)
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                             advertising))
                lcl_adv |= ADVERTISE_PAUSE_ASYM;
 
        return lcl_adv;
index dca6ab4..36e412c 100644 (file)
@@ -226,6 +226,7 @@ enum {
        MLX4_DEV_CAP_FLAG2_SL_TO_VL_CHANGE_EVENT = 1ULL << 37,
        MLX4_DEV_CAP_FLAG2_USER_MAC_EN          = 1ULL << 38,
        MLX4_DEV_CAP_FLAG2_DRIVER_VERSION_TO_FW = 1ULL << 39,
+       MLX4_DEV_CAP_FLAG2_SW_CQ_INIT           = 1ULL << 40,
 };
 
 enum {
@@ -1136,7 +1137,8 @@ void mlx4_free_hwq_res(struct mlx4_dev *mdev, struct mlx4_hwq_resources *wqres,
 
 int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
                  struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq,
-                 unsigned vector, int collapsed, int timestamp_en);
+                 unsigned int vector, int collapsed, int timestamp_en,
+                 void *buf_addr, bool user_cq);
 void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq);
 int mlx4_qp_reserve_range(struct mlx4_dev *dev, int cnt, int align,
                          int *base, u8 flags, u8 usage);
index 857f8ab..fc6ba71 100644 (file)
@@ -845,6 +845,8 @@ enum tc_setup_type {
        TC_SETUP_QDISC_PRIO,
        TC_SETUP_QDISC_MQ,
        TC_SETUP_QDISC_ETF,
+       TC_SETUP_ROOT_QDISC,
+       TC_SETUP_QDISC_GRED,
 };
 
 /* These structures hold the attributes of bpf state that are being passed
@@ -863,9 +865,6 @@ enum bpf_netdev_command {
        XDP_QUERY_PROG,
        XDP_QUERY_PROG_HW,
        /* BPF program for offload callbacks, invoked at program load time. */
-       BPF_OFFLOAD_VERIFIER_PREP,
-       BPF_OFFLOAD_TRANSLATE,
-       BPF_OFFLOAD_DESTROY,
        BPF_OFFLOAD_MAP_ALLOC,
        BPF_OFFLOAD_MAP_FREE,
        XDP_QUERY_XSK_UMEM,
@@ -891,15 +890,6 @@ struct netdev_bpf {
                        /* flags with which program was installed */
                        u32 prog_flags;
                };
-               /* BPF_OFFLOAD_VERIFIER_PREP */
-               struct {
-                       struct bpf_prog *prog;
-                       const struct bpf_prog_offload_ops *ops; /* callee set */
-               } verifier;
-               /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */
-               struct {
-                       struct bpf_prog *prog;
-               } offload;
                /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */
                struct {
                        struct bpf_offloaded_map *offmap;
@@ -2388,13 +2378,13 @@ struct pcpu_sw_netstats {
        u64     tx_packets;
        u64     tx_bytes;
        struct u64_stats_sync   syncp;
-};
+} __aligned(4 * sizeof(u64));
 
 struct pcpu_lstats {
        u64 packets;
        u64 bytes;
        struct u64_stats_sync syncp;
-};
+} __aligned(2 * sizeof(u64));
 
 #define __netdev_alloc_pcpu_stats(type, gfp)                           \
 ({                                                                     \
@@ -2615,7 +2605,7 @@ struct net_device *dev_get_by_name(struct net *net, const char *name);
 struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
 struct net_device *__dev_get_by_name(struct net *net, const char *name);
 int dev_alloc_name(struct net_device *dev, const char *name);
-int dev_open(struct net_device *dev);
+int dev_open(struct net_device *dev, struct netlink_ext_ack *extack);
 void dev_close(struct net_device *dev);
 void dev_close_many(struct list_head *head, bool unlink);
 void dev_disable_lro(struct net_device *dev);
@@ -3224,6 +3214,14 @@ static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes)
        netdev_tx_sent_queue(netdev_get_tx_queue(dev, 0), bytes);
 }
 
+static inline bool __netdev_sent_queue(struct net_device *dev,
+                                      unsigned int bytes,
+                                      bool xmit_more)
+{
+       return __netdev_tx_sent_queue(netdev_get_tx_queue(dev, 0), bytes,
+                                     xmit_more);
+}
+
 static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue,
                                             unsigned int pkts, unsigned int bytes)
 {
@@ -3613,8 +3611,10 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
 int dev_ifconf(struct net *net, struct ifconf *, int);
 int dev_ethtool(struct net *net, struct ifreq *);
 unsigned int dev_get_flags(const struct net_device *);
-int __dev_change_flags(struct net_device *, unsigned int flags);
-int dev_change_flags(struct net_device *, unsigned int);
+int __dev_change_flags(struct net_device *dev, unsigned int flags,
+                      struct netlink_ext_ack *extack);
+int dev_change_flags(struct net_device *dev, unsigned int flags,
+                    struct netlink_ext_ack *extack);
 void __dev_notify_flags(struct net_device *, unsigned int old_flags,
                        unsigned int gchanges);
 int dev_change_name(struct net_device *, const char *);
@@ -4068,6 +4068,16 @@ int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
                       int (*sync)(struct net_device *, const unsigned char *),
                       int (*unsync)(struct net_device *,
                                     const unsigned char *));
+int __hw_addr_ref_sync_dev(struct netdev_hw_addr_list *list,
+                          struct net_device *dev,
+                          int (*sync)(struct net_device *,
+                                      const unsigned char *, int),
+                          int (*unsync)(struct net_device *,
+                                        const unsigned char *, int));
+void __hw_addr_ref_unsync_dev(struct netdev_hw_addr_list *list,
+                             struct net_device *dev,
+                             int (*unsync)(struct net_device *,
+                                           const unsigned char *, int));
 void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
                          struct net_device *dev,
                          int (*unsync)(struct net_device *,
@@ -4332,9 +4342,10 @@ static inline bool can_checksum_protocol(netdev_features_t features,
 }
 
 #ifdef CONFIG_BUG
-void netdev_rx_csum_fault(struct net_device *dev);
+void netdev_rx_csum_fault(struct net_device *dev, struct sk_buff *skb);
 #else
-static inline void netdev_rx_csum_fault(struct net_device *dev)
+static inline void netdev_rx_csum_fault(struct net_device *dev,
+                                       struct sk_buff *skb)
 {
 }
 #endif
@@ -4360,7 +4371,7 @@ static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_devi
                                            struct netdev_queue *txq, bool more)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
-       int rc;
+       netdev_tx_t rc;
 
        rc = __netdev_start_xmit(ops, skb, dev, more);
        if (rc == NETDEV_TX_OK)
diff --git a/include/linux/objagg.h b/include/linux/objagg.h
new file mode 100644 (file)
index 0000000..34f38c1
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#ifndef _OBJAGG_H
+#define _OBJAGG_H
+
+struct objagg_ops {
+       size_t obj_size;
+       void * (*delta_create)(void *priv, void *parent_obj, void *obj);
+       void (*delta_destroy)(void *priv, void *delta_priv);
+       void * (*root_create)(void *priv, void *obj);
+       void (*root_destroy)(void *priv, void *root_priv);
+};
+
+struct objagg;
+struct objagg_obj;
+
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj);
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj);
+const void *objagg_obj_raw(const struct objagg_obj *objagg_obj);
+
+struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj);
+void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj);
+struct objagg *objagg_create(const struct objagg_ops *ops, void *priv);
+void objagg_destroy(struct objagg *objagg);
+
+struct objagg_obj_stats {
+       unsigned int user_count;
+       unsigned int delta_user_count; /* includes delta object users */
+};
+
+struct objagg_obj_stats_info {
+       struct objagg_obj_stats stats;
+       struct objagg_obj *objagg_obj; /* associated object */
+       bool is_root;
+};
+
+struct objagg_stats {
+       unsigned int stats_info_count;
+       struct objagg_obj_stats_info stats_info[];
+};
+
+const struct objagg_stats *objagg_stats_get(struct objagg *objagg);
+void objagg_stats_put(const struct objagg_stats *objagg_stats);
+
+#endif
index 90d81ee..9cd72aa 100644 (file)
@@ -13,7 +13,6 @@
 struct net_device;
 extern int of_get_phy_mode(struct device_node *np);
 extern const void *of_get_mac_address(struct device_node *np);
-extern int of_get_nvmem_mac_address(struct device_node *np, void *addr);
 extern struct net_device *of_find_net_device_by_node(struct device_node *np);
 #else
 static inline int of_get_phy_mode(struct device_node *np)
@@ -26,11 +25,6 @@ static inline const void *of_get_mac_address(struct device_node *np)
        return NULL;
 }
 
-static inline int of_get_nvmem_mac_address(struct device_node *np, void *addr)
-{
-       return -ENODEV;
-}
-
 static inline struct net_device *of_find_net_device_by_node(struct device_node *np)
 {
        return NULL;
index 69f0abe..144de2e 100644 (file)
 
 #define PCI_VENDOR_ID_SYNOPSYS         0x16c3
 
+#define PCI_VENDOR_ID_USR              0x16ec
+
 #define PCI_VENDOR_ID_VITESSE          0x1725
 #define PCI_DEVICE_ID_VITESSE_VSC7174  0x7174
 
index 3ea87f7..8f92724 100644 (file)
@@ -58,6 +58,11 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini
 #define PHY_10GBIT_FEATURES ((unsigned long *)&phy_10gbit_features)
 #define PHY_10GBIT_FULL_FEATURES ((unsigned long *)&phy_10gbit_full_features)
 
+extern const int phy_10_100_features_array[4];
+extern const int phy_basic_t1_features_array[2];
+extern const int phy_gbit_features_array[2];
+extern const int phy_10gbit_features_array[1];
+
 /*
  * Set phydev->irq to PHY_POLL if interrupts are not supported,
  * or not desired for this PHY.  Set to PHY_IGNORE_INTERRUPT if
@@ -66,9 +71,8 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini
 #define PHY_POLL               -1
 #define PHY_IGNORE_INTERRUPT   -2
 
-#define PHY_HAS_INTERRUPT      0x00000001
-#define PHY_IS_INTERNAL                0x00000002
-#define PHY_RST_AFTER_CLK_EN   0x00000004
+#define PHY_IS_INTERNAL                0x00000001
+#define PHY_RST_AFTER_CLK_EN   0x00000002
 #define MDIO_DEVICE_IS_PHY     0x80000000
 
 /* Interface Mode definitions */
@@ -178,7 +182,6 @@ static inline const char *phy_modes(phy_interface_t interface)
 #define PHY_INIT_TIMEOUT       100000
 #define PHY_STATE_TIME         1
 #define PHY_FORCE_TIMEOUT      10
-#define PHY_AN_TIMEOUT         10
 
 #define PHY_MAX_ADDR   32
 
@@ -264,57 +267,27 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
 void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 
-#define PHY_INTERRUPT_DISABLED 0x0
-#define PHY_INTERRUPT_ENABLED  0x80000000
+#define PHY_INTERRUPT_DISABLED false
+#define PHY_INTERRUPT_ENABLED  true
 
 /* PHY state machine states:
  *
  * DOWN: PHY device and driver are not ready for anything.  probe
  * should be called if and only if the PHY is in this state,
  * given that the PHY device exists.
- * - PHY driver probe function will, depending on the PHY, set
- * the state to STARTING or READY
- *
- * STARTING:  PHY device is coming up, and the ethernet driver is
- * not ready.  PHY drivers may set this in the probe function.
- * If they do, they are responsible for making sure the state is
- * eventually set to indicate whether the PHY is UP or READY,
- * depending on the state when the PHY is done starting up.
- * - PHY driver will set the state to READY
- * - start will set the state to PENDING
+ * - PHY driver probe function will set the state to READY
  *
  * READY: PHY is ready to send and receive packets, but the
  * controller is not.  By default, PHYs which do not implement
- * probe will be set to this state by phy_probe().  If the PHY
- * driver knows the PHY is ready, and the PHY state is STARTING,
- * then it sets this STATE.
+ * probe will be set to this state by phy_probe().
  * - start will set the state to UP
  *
- * PENDING: PHY device is coming up, but the ethernet driver is
- * ready.  phy_start will set this state if the PHY state is
- * STARTING.
- * - PHY driver will set the state to UP when the PHY is ready
- *
  * UP: The PHY and attached device are ready to do work.
  * Interrupts should be started here.
- * - timer moves to AN
- *
- * AN: The PHY is currently negotiating the link state.  Link is
- * therefore down for now.  phy_timer will set this state when it
- * detects the state is UP.  config_aneg will set this state
- * whenever called with phydev->autoneg set to AUTONEG_ENABLE.
- * - If autonegotiation finishes, but there's no link, it sets
- *   the state to NOLINK.
- * - If aneg finishes with link, it sets the state to RUNNING,
- *   and calls adjust_link
- * - If autonegotiation did not finish after an arbitrary amount
- *   of time, autonegotiation should be tried again if the PHY
- *   supports "magic" autonegotiation (back to AN)
- * - If it didn't finish, and no magic_aneg, move to FORCING.
+ * - timer moves to NOLINK or RUNNING
  *
  * NOLINK: PHY is up, but not currently plugged in.
- * - If the timer notes that the link comes back, we move to RUNNING
- * - config_aneg moves to AN
+ * - irq or timer will set RUNNING if link comes back
  * - phy_stop moves to HALTED
  *
  * FORCING: PHY is being configured with forced settings
@@ -325,11 +298,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
  *
  * RUNNING: PHY is currently up, running, and possibly sending
  * and/or receiving packets
- * - timer will set CHANGELINK if we're polling (this ensures the
- *   link state is polled every other cycle of this state machine,
- *   which makes it every other second)
- * - irq will set CHANGELINK
- * - config_aneg will set AN
+ * - irq or timer will set NOLINK if link goes down
  * - phy_stop moves to HALTED
  *
  * CHANGELINK: PHY experienced a change in link state
@@ -349,11 +318,8 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
  */
 enum phy_state {
        PHY_DOWN = 0,
-       PHY_STARTING,
        PHY_READY,
-       PHY_PENDING,
        PHY_UP,
-       PHY_AN,
        PHY_RUNNING,
        PHY_NOLINK,
        PHY_FORCING,
@@ -390,7 +356,6 @@ struct phy_c45_device_ids {
  * giving up on the current attempt at acquiring a link
  * irq: IRQ number of the PHY's interrupt (-1 if none)
  * phy_timer: The timer for handling the state machine
- * phy_queue: A work_queue for the phy_mac_interrupt
  * attached_dev: The attached enet driver's device instance ptr
  * adjust_link: Callback for the enet controller to respond to
  * changes in the link state.
@@ -427,6 +392,9 @@ struct phy_device {
        /* The most recently read link state */
        unsigned link:1;
 
+       /* Interrupts are enabled */
+       unsigned interrupts:1;
+
        enum phy_state state;
 
        u32 dev_flags;
@@ -442,14 +410,11 @@ struct phy_device {
        int pause;
        int asym_pause;
 
-       /* Enabled Interrupts */
-       u32 interrupts;
-
-       /* Union of PHY and Attached devices' supported modes */
-       /* See mii.h for more info */
-       u32 supported;
-       u32 advertising;
-       u32 lp_advertising;
+       /* Union of PHY and Attached devices' supported link modes */
+       /* See ethtool.h for more info */
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
 
        /* Energy efficient ethernet modes which should be prohibited */
        u32 eee_broken_modes;
@@ -475,7 +440,6 @@ struct phy_device {
        void *priv;
 
        /* Interrupt and Polling infrastructure */
-       struct work_struct phy_queue;
        struct delayed_work state_queue;
 
        struct mutex lock;
@@ -674,6 +638,10 @@ struct phy_driver {
 #define PHY_ANY_ID "MATCH ANY PHY"
 #define PHY_ANY_UID 0xffffffff
 
+#define PHY_ID_MATCH_EXACT(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 0)
+#define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4)
+#define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10)
+
 /* A Structure for boards to register fixups with the PHY Lib */
 struct phy_fixup {
        struct list_head list;
@@ -697,9 +665,9 @@ struct phy_setting {
 
 const struct phy_setting *
 phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
-                  size_t maxbit, bool exact);
+                  bool exact);
 size_t phy_speeds(unsigned int *speeds, size_t size,
-                 unsigned long *mask, size_t maxbit);
+                 unsigned long *mask);
 
 void phy_resolve_aneg_linkmode(struct phy_device *phydev);
 
@@ -1050,11 +1018,9 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
 int phy_drivers_register(struct phy_driver *new_driver, int n,
                         struct module *owner);
 void phy_state_machine(struct work_struct *work);
-void phy_change_work(struct work_struct *work);
 void phy_mac_interrupt(struct phy_device *phydev);
 void phy_start_machine(struct phy_device *phydev);
 void phy_stop_machine(struct phy_device *phydev);
-void phy_trigger_machine(struct phy_device *phydev);
 int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
 void phy_ethtool_ksettings_get(struct phy_device *phydev,
                               struct ethtool_link_ksettings *cmd);
index b37b05b..4587ce3 100644 (file)
@@ -20,7 +20,7 @@ struct phy_device;
 #include <linux/leds.h>
 #include <linux/phy.h>
 
-#define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE      10
+#define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE      11
 
 #define PHY_LINK_LED_TRIGGER_NAME_SIZE (MII_BUS_ID_SIZE + \
                                       FIELD_SIZEOF(struct mdio_device, addr)+\
diff --git a/include/linux/platform_data/mdio-gpio.h b/include/linux/platform_data/mdio-gpio.h
new file mode 100644 (file)
index 0000000..13874fa
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MDIO-GPIO bus platform data structure
+ */
+
+#ifndef __LINUX_MDIO_GPIO_PDATA_H
+#define __LINUX_MDIO_GPIO_PDATA_H
+
+struct mdio_gpio_platform_data {
+       u32 phy_mask;
+       u32 phy_ignore_ta_mask;
+};
+
+#endif /* __LINUX_MDIO_GPIO_PDATA_H */
index 51349d1..7121bbe 100644 (file)
@@ -39,6 +39,15 @@ struct ptp_clock_request {
 };
 
 struct system_device_crosststamp;
+
+/**
+ * struct ptp_system_timestamp - system time corresponding to a PHC timestamp
+ */
+struct ptp_system_timestamp {
+       struct timespec64 pre_ts;
+       struct timespec64 post_ts;
+};
+
 /**
  * struct ptp_clock_info - decribes a PTP hardware clock
  *
@@ -73,8 +82,18 @@ struct system_device_crosststamp;
  *            parameter delta: Desired change in nanoseconds.
  *
  * @gettime64:  Reads the current time from the hardware clock.
+ *              This method is deprecated.  New drivers should implement
+ *              the @gettimex64 method instead.
  *              parameter ts: Holds the result.
  *
+ * @gettimex64:  Reads the current time from the hardware clock and optionally
+ *               also the system clock.
+ *               parameter ts: Holds the PHC timestamp.
+ *               parameter sts: If not NULL, it holds a pair of timestamps from
+ *               the system clock. The first reading is made right before
+ *               reading the lowest bits of the PHC timestamp and the second
+ *               reading immediately follows that.
+ *
  * @getcrosststamp:  Reads the current time from the hardware clock and
  *                   system clock simultaneously.
  *                   parameter cts: Contains timestamp (device,system) pair,
@@ -124,6 +143,8 @@ struct ptp_clock_info {
        int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
        int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
        int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
+       int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
+                         struct ptp_system_timestamp *sts);
        int (*getcrosststamp)(struct ptp_clock_info *ptp,
                              struct system_device_crosststamp *cts);
        int (*settime64)(struct ptp_clock_info *p, const struct timespec64 *ts);
@@ -247,4 +268,16 @@ static inline int ptp_schedule_worker(struct ptp_clock *ptp,
 
 #endif
 
+static inline void ptp_read_system_prets(struct ptp_system_timestamp *sts)
+{
+       if (sts)
+               ktime_get_real_ts64(&sts->pre_ts);
+}
+
+static inline void ptp_read_system_postts(struct ptp_system_timestamp *sts)
+{
+       if (sts)
+               ktime_get_real_ts64(&sts->post_ts);
+}
+
 #endif
index a47321a..91c536a 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/slab.h>
 #include <linux/qed/common_hsi.h>
 #include <linux/qed/qed_chain.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
 
 enum dcbx_protocol_type {
        DCBX_PROTOCOL_ISCSI,
@@ -448,11 +449,24 @@ struct qed_mfw_tlv_iscsi {
        bool tx_bytes_set;
 };
 
+enum qed_db_rec_width {
+       DB_REC_WIDTH_32B,
+       DB_REC_WIDTH_64B,
+};
+
+enum qed_db_rec_space {
+       DB_REC_KERNEL,
+       DB_REC_USER,
+};
+
 #define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
                                            (void __iomem *)(reg_addr))
 
 #define DIRECT_REG_RD(reg_addr) readl((void __iomem *)(reg_addr))
 
+#define DIRECT_REG_WR64(reg_addr, val) writeq((u32)val,        \
+                                             (void __iomem *)(reg_addr))
+
 #define QED_COALESCE_MAX 0x1FF
 #define QED_DEFAULT_RX_USECS 12
 #define QED_DEFAULT_TX_USECS 48
@@ -1015,6 +1029,33 @@ struct qed_common_ops {
  */
        int (*set_led)(struct qed_dev *cdev,
                       enum qed_led_mode mode);
+/**
+ * @brief db_recovery_add - add doorbell information to the doorbell
+ * recovery mechanism.
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address of where db_data is stored
+ * @param db_is_32b - doorbell is 32b pr 64b
+ * @param db_is_user - doorbell recovery addresses are user or kernel space
+ */
+       int (*db_recovery_add)(struct qed_dev *cdev,
+                              void __iomem *db_addr,
+                              void *db_data,
+                              enum qed_db_rec_width db_width,
+                              enum qed_db_rec_space db_space);
+
+/**
+ * @brief db_recovery_del - remove doorbell information from the doorbell
+ * recovery mechanism. db_data serves as key (db_addr is not unique).
+ *
+ * @param cdev
+ * @param db_addr - doorbell address
+ * @param db_data - address where db_data is stored. Serves as key for the
+ *                 entry to delete.
+ */
+       int (*db_recovery_del)(struct qed_dev *cdev,
+                              void __iomem *db_addr, void *db_data);
 
 /**
  * @brief update_drv_state - API to inform the change in the driver state.
index eb71110..20f9c6a 100644 (file)
@@ -75,8 +75,19 @@ struct bucket_table {
        struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp;
 };
 
+/*
+ * NULLS_MARKER() expects a hash value with the low
+ * bits mostly likely to be significant, and it discards
+ * the msb.
+ * We git it an address, in which the bottom 2 bits are
+ * always 0, and the msb might be significant.
+ * So we shift the address down one bit to align with
+ * expectations and avoid losing a significant bit.
+ */
+#define        RHT_NULLS_MARKER(ptr)   \
+       ((void *)NULLS_MARKER(((unsigned long) (ptr)) >> 1))
 #define INIT_RHT_NULLS_HEAD(ptr)       \
-       ((ptr) = (typeof(ptr)) NULLS_MARKER(0))
+       ((ptr) = RHT_NULLS_MARKER(&(ptr)))
 
 static inline bool rht_is_a_nulls(const struct rhash_head *ptr)
 {
@@ -471,6 +482,7 @@ static inline struct rhash_head *__rhashtable_lookup(
                .ht = ht,
                .key = key,
        };
+       struct rhash_head __rcu * const *head;
        struct bucket_table *tbl;
        struct rhash_head *he;
        unsigned int hash;
@@ -478,13 +490,19 @@ static inline struct rhash_head *__rhashtable_lookup(
        tbl = rht_dereference_rcu(ht->tbl, ht);
 restart:
        hash = rht_key_hashfn(ht, tbl, key, params);
-       rht_for_each_rcu(he, tbl, hash) {
-               if (params.obj_cmpfn ?
-                   params.obj_cmpfn(&arg, rht_obj(ht, he)) :
-                   rhashtable_compare(&arg, rht_obj(ht, he)))
-                       continue;
-               return he;
-       }
+       head = rht_bucket(tbl, hash);
+       do {
+               rht_for_each_rcu_continue(he, *head, tbl, hash) {
+                       if (params.obj_cmpfn ?
+                           params.obj_cmpfn(&arg, rht_obj(ht, he)) :
+                           rhashtable_compare(&arg, rht_obj(ht, he)))
+                               continue;
+                       return he;
+               }
+               /* An object might have been moved to a different hash chain,
+                * while we walk along it - better check and retry.
+                */
+       } while (he != RHT_NULLS_MARKER(head));
 
        /* Ensure we see any new tables. */
        smp_rmb();
index 0d1b2c3..b1831a5 100644 (file)
@@ -481,10 +481,11 @@ static inline void sock_zerocopy_get(struct ubuf_info *uarg)
 }
 
 void sock_zerocopy_put(struct ubuf_info *uarg);
-void sock_zerocopy_put_abort(struct ubuf_info *uarg);
+void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref);
 
 void sock_zerocopy_callback(struct ubuf_info *uarg, bool success);
 
+int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len);
 int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                             struct msghdr *msg, int len,
                             struct ubuf_info *uarg);
@@ -615,6 +616,8 @@ typedef unsigned char *sk_buff_data_t;
  *     @pkt_type: Packet class
  *     @fclone: skbuff clone status
  *     @ipvs_property: skbuff is owned by ipvs
+ *     @offload_fwd_mark: Packet was L2-forwarded in hardware
+ *     @offload_l3_fwd_mark: Packet was L3-forwarded in hardware
  *     @tc_skip_classify: do not classify packet. set by IFB device
  *     @tc_at_ingress: used within tc_classify to distinguish in/egress
  *     @tc_redirected: packet was redirected by a tc action
@@ -777,6 +780,14 @@ struct sk_buff {
        __u8                    encap_hdr_csum:1;
        __u8                    csum_valid:1;
 
+#ifdef __BIG_ENDIAN_BITFIELD
+#define PKT_VLAN_PRESENT_BIT   7
+#else
+#define PKT_VLAN_PRESENT_BIT   0
+#endif
+#define PKT_VLAN_PRESENT_OFFSET()      offsetof(struct sk_buff, __pkt_vlan_present_offset)
+       __u8                    __pkt_vlan_present_offset[0];
+       __u8                    vlan_present:1;
        __u8                    csum_complete_sw:1;
        __u8                    csum_level:2;
        __u8                    csum_not_inet:1;
@@ -784,13 +795,13 @@ struct sk_buff {
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
        __u8                    ndisc_nodetype:2;
 #endif
-       __u8                    ipvs_property:1;
 
+       __u8                    ipvs_property:1;
        __u8                    inner_protocol_type:1;
        __u8                    remcsum_offload:1;
 #ifdef CONFIG_NET_SWITCHDEV
        __u8                    offload_fwd_mark:1;
-       __u8                    offload_mr_fwd_mark:1;
+       __u8                    offload_l3_fwd_mark:1;
 #endif
 #ifdef CONFIG_NET_CLS_ACT
        __u8                    tc_skip_classify:1;
@@ -1317,10 +1328,14 @@ static inline struct ubuf_info *skb_zcopy(struct sk_buff *skb)
        return is_zcopy ? skb_uarg(skb) : NULL;
 }
 
-static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg)
+static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg,
+                                bool *have_ref)
 {
        if (skb && uarg && !skb_zcopy(skb)) {
-               sock_zerocopy_get(uarg);
+               if (unlikely(have_ref && *have_ref))
+                       *have_ref = false;
+               else
+                       sock_zerocopy_get(uarg);
                skb_shinfo(skb)->destructor_arg = uarg;
                skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG;
        }
@@ -1365,7 +1380,7 @@ static inline void skb_zcopy_abort(struct sk_buff *skb)
        struct ubuf_info *uarg = skb_zcopy(skb);
 
        if (uarg) {
-               sock_zerocopy_put_abort(uarg);
+               sock_zerocopy_put_abort(uarg, false);
                skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG;
        }
 }
@@ -1741,8 +1756,6 @@ static inline void skb_queue_head_init_class(struct sk_buff_head *list,
  *     The "__skb_xxxx()" functions are the non-atomic ones that
  *     can only be called with interrupts disabled.
  */
-void skb_insert(struct sk_buff *old, struct sk_buff *newsk,
-               struct sk_buff_head *list);
 static inline void __skb_insert(struct sk_buff *newsk,
                                struct sk_buff *prev, struct sk_buff *next,
                                struct sk_buff_head *list)
@@ -2524,10 +2537,8 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len);
 
 static inline void __skb_set_length(struct sk_buff *skb, unsigned int len)
 {
-       if (unlikely(skb_is_nonlinear(skb))) {
-               WARN_ON(1);
+       if (WARN_ON(skb_is_nonlinear(skb)))
                return;
-       }
        skb->len = len;
        skb_set_tail_pointer(skb, len);
 }
@@ -3345,7 +3356,6 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
                    unsigned int flags);
 int skb_send_sock_locked(struct sock *sk, struct sk_buff *skb, int offset,
                         int len);
-int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len);
 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
 unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
 int skb_zerocopy(struct sk_buff *to, struct sk_buff *from,
index 320d49d..2725c83 100644 (file)
@@ -49,7 +49,13 @@ struct udp_sock {
        unsigned int     corkflag;      /* Cork is required */
        __u8             encap_type;    /* Is this an Encapsulation socket? */
        unsigned char    no_check6_tx:1,/* Send zero UDP6 checksums on TX? */
-                        no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */
+                        no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */
+                        encap_enabled:1, /* This socket enabled encap
+                                          * processing; UDP tunnels and
+                                          * different encapsulation layer set
+                                          * this
+                                          */
+                        gro_enabled:1; /* Can accept GRO packets */
        /*
         * Following member retains the information to create a UDP header
         * when the socket is uncorked.
@@ -71,6 +77,7 @@ struct udp_sock {
         * For encapsulation sockets.
         */
        int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
+       int (*encap_err_lookup)(struct sock *sk, struct sk_buff *skb);
        void (*encap_destroy)(struct sock *sk);
 
        /* GRO functions for UDP socket */
@@ -115,6 +122,23 @@ static inline bool udp_get_no_check6_rx(struct sock *sk)
        return udp_sk(sk)->no_check6_rx;
 }
 
+static inline void udp_cmsg_recv(struct msghdr *msg, struct sock *sk,
+                                struct sk_buff *skb)
+{
+       int gso_size;
+
+       if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) {
+               gso_size = skb_shinfo(skb)->gso_size;
+               put_cmsg(msg, SOL_UDP, UDP_GRO, sizeof(gso_size), &gso_size);
+       }
+}
+
+static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb)
+{
+       return !udp_sk(sk)->gro_enabled && skb_is_gso(skb) &&
+              skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4;
+}
+
 #define udp_portaddr_for_each_entry(__sk, list) \
        hlist_for_each_entry(__sk, list, __sk_common.skc_portaddr_node)
 
index 45db0c7..67f4293 100644 (file)
@@ -365,6 +365,7 @@ enum devlink_param_generic_id {
        DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
        DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
        DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
+       DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
 
        /* add new param generic ids above here*/
        __DEVLINK_PARAM_GENERIC_ID_MAX,
@@ -392,6 +393,9 @@ enum devlink_param_generic_id {
 #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME "msix_vec_per_pf_min"
 #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE DEVLINK_PARAM_TYPE_U32
 
+#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME "fw_load_policy"
+#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE DEVLINK_PARAM_TYPE_U8
+
 #define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate)     \
 {                                                                      \
        .id = DEVLINK_PARAM_GENERIC_ID_##_id,                           \
index 23690c4..6ee2e24 100644 (file)
@@ -113,6 +113,7 @@ struct dsa_device_ops {
                               struct packet_type *pt);
        int (*flow_dissect)(const struct sk_buff *skb, __be16 *proto,
                            int *offset);
+       unsigned int overhead;
 };
 
 struct dsa_switch_tree {
index 8ce2179..93f2c9a 100644 (file)
@@ -38,8 +38,8 @@ struct flowi_common {
 #define FLOWI_FLAG_KNOWN_NH            0x02
 #define FLOWI_FLAG_SKIP_NH_OIF         0x04
        __u32   flowic_secid;
-       struct flowi_tunnel flowic_tun_key;
        kuid_t  flowic_uid;
+       struct flowi_tunnel flowic_tun_key;
 };
 
 union flowi_uli {
index 6a4586d..2b26979 100644 (file)
@@ -209,8 +209,8 @@ enum flow_dissector_key_id {
        FLOW_DISSECTOR_KEY_ETH_ADDRS, /* struct flow_dissector_key_eth_addrs */
        FLOW_DISSECTOR_KEY_TIPC, /* struct flow_dissector_key_tipc */
        FLOW_DISSECTOR_KEY_ARP, /* struct flow_dissector_key_arp */
-       FLOW_DISSECTOR_KEY_VLAN, /* struct flow_dissector_key_flow_vlan */
-       FLOW_DISSECTOR_KEY_FLOW_LABEL, /* struct flow_dissector_key_flow_tags */
+       FLOW_DISSECTOR_KEY_VLAN, /* struct flow_dissector_key_vlan */
+       FLOW_DISSECTOR_KEY_FLOW_LABEL, /* struct flow_dissector_key_tags */
        FLOW_DISSECTOR_KEY_GRE_KEYID, /* struct flow_dissector_key_keyid */
        FLOW_DISSECTOR_KEY_MPLS_ENTROPY, /* struct flow_dissector_key_keyid */
        FLOW_DISSECTOR_KEY_ENC_KEYID, /* struct flow_dissector_key_keyid */
@@ -221,7 +221,7 @@ enum flow_dissector_key_id {
        FLOW_DISSECTOR_KEY_MPLS, /* struct flow_dissector_key_mpls */
        FLOW_DISSECTOR_KEY_TCP, /* struct flow_dissector_key_tcp */
        FLOW_DISSECTOR_KEY_IP, /* struct flow_dissector_key_ip */
-       FLOW_DISSECTOR_KEY_CVLAN, /* struct flow_dissector_key_flow_vlan */
+       FLOW_DISSECTOR_KEY_CVLAN, /* struct flow_dissector_key_vlan */
        FLOW_DISSECTOR_KEY_ENC_IP, /* struct flow_dissector_key_ip */
        FLOW_DISSECTOR_KEY_ENC_OPTS, /* struct flow_dissector_key_enc_opts */
 
index 946bd53..ca23860 100644 (file)
@@ -10,7 +10,7 @@
 struct gnet_stats_basic_cpu {
        struct gnet_stats_basic_packed bstats;
        struct u64_stats_sync syncp;
-};
+} __aligned(2 * sizeof(u64));
 
 struct net_rate_estimator;
 
index a7600ed..fc6a7e0 100644 (file)
@@ -60,6 +60,12 @@ struct genevehdr {
        struct geneve_opt options[];
 };
 
+static inline bool netif_is_geneve(const struct net_device *dev)
+{
+       return dev->rtnl_link_ops &&
+              !strcmp(dev->rtnl_link_ops->kind, "geneve");
+}
+
 #ifdef CONFIG_INET
 struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
                                        u8 name_assign_type, u16 dst_port);
index 3ef2743..6ac3a5b 100644 (file)
@@ -41,7 +41,7 @@ struct net;
 
 void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info);
 int icmp_rcv(struct sk_buff *skb);
-void icmp_err(struct sk_buff *skb, u32 info);
+int icmp_err(struct sk_buff *skb, u32 info);
 int icmp_init(void);
 void icmp_out_count(struct net *net, unsigned char type);
 
index 6e91e38..9db98af 100644 (file)
@@ -115,9 +115,8 @@ int inet6_hash(struct sock *sk);
         ((__sk)->sk_family == AF_INET6)                        &&      \
         ipv6_addr_equal(&(__sk)->sk_v6_daddr, (__saddr))               &&      \
         ipv6_addr_equal(&(__sk)->sk_v6_rcv_saddr, (__daddr))   &&      \
-        (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif)) ||                      \
-          ((__sk)->sk_bound_dev_if == (__sdif)))               &&      \
+        (((__sk)->sk_bound_dev_if == (__dif))  ||                      \
+         ((__sk)->sk_bound_dev_if == (__sdif)))                &&      \
         net_eq(sock_net(__sk), (__net)))
 
 #endif /* _INET6_HASHTABLES_H */
index 9141e95..0ce460e 100644 (file)
@@ -79,6 +79,7 @@ struct inet_ehash_bucket {
 
 struct inet_bind_bucket {
        possible_net_t          ib_net;
+       int                     l3mdev;
        unsigned short          port;
        signed char             fastreuse;
        signed char             fastreuseport;
@@ -188,10 +189,21 @@ static inline void inet_ehash_locks_free(struct inet_hashinfo *hashinfo)
        hashinfo->ehash_locks = NULL;
 }
 
+static inline bool inet_sk_bound_dev_eq(struct net *net, int bound_dev_if,
+                                       int dif, int sdif)
+{
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+       return inet_bound_dev_eq(!!net->ipv4.sysctl_tcp_l3mdev_accept,
+                                bound_dev_if, dif, sdif);
+#else
+       return inet_bound_dev_eq(true, bound_dev_if, dif, sdif);
+#endif
+}
+
 struct inet_bind_bucket *
 inet_bind_bucket_create(struct kmem_cache *cachep, struct net *net,
                        struct inet_bind_hashbucket *head,
-                       const unsigned short snum);
+                       const unsigned short snum, int l3mdev);
 void inet_bind_bucket_destroy(struct kmem_cache *cachep,
                              struct inet_bind_bucket *tb);
 
@@ -282,9 +294,8 @@ static inline struct sock *inet_lookup_listener(struct net *net,
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif, __sdif) \
        (((__sk)->sk_portpair == (__ports))                     &&      \
         ((__sk)->sk_addrpair == (__cookie))                    &&      \
-        (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif))                 ||      \
-          ((__sk)->sk_bound_dev_if == (__sdif)))               &&      \
+        (((__sk)->sk_bound_dev_if == (__dif))                  ||      \
+         ((__sk)->sk_bound_dev_if == (__sdif)))                &&      \
         net_eq(sock_net(__sk), (__net)))
 #else /* 32-bit arch */
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
@@ -294,9 +305,8 @@ static inline struct sock *inet_lookup_listener(struct net *net,
        (((__sk)->sk_portpair == (__ports))             &&              \
         ((__sk)->sk_daddr      == (__saddr))           &&              \
         ((__sk)->sk_rcv_saddr  == (__daddr))           &&              \
-        (!(__sk)->sk_bound_dev_if      ||                              \
-          ((__sk)->sk_bound_dev_if == (__dif))         ||              \
-          ((__sk)->sk_bound_dev_if == (__sdif)))       &&              \
+        (((__sk)->sk_bound_dev_if == (__dif))          ||              \
+         ((__sk)->sk_bound_dev_if == (__sdif)))        &&              \
         net_eq(sock_net(__sk), (__net)))
 #endif /* 64-bit arch */
 
index a80fd0a..e8eef85 100644 (file)
@@ -130,6 +130,27 @@ static inline int inet_request_bound_dev_if(const struct sock *sk,
        return sk->sk_bound_dev_if;
 }
 
+static inline int inet_sk_bound_l3mdev(const struct sock *sk)
+{
+#ifdef CONFIG_NET_L3_MASTER_DEV
+       struct net *net = sock_net(sk);
+
+       if (!net->ipv4.sysctl_tcp_l3mdev_accept)
+               return l3mdev_master_ifindex_by_index(net,
+                                                     sk->sk_bound_dev_if);
+#endif
+
+       return 0;
+}
+
+static inline bool inet_bound_dev_eq(bool l3mdev_accept, int bound_dev_if,
+                                    int dif, int sdif)
+{
+       if (!bound_dev_if)
+               return !sdif || l3mdev_accept;
+       return bound_dev_if == dif || bound_dev_if == sdif;
+}
+
 struct inet_cork {
        unsigned int            flags;
        __be32                  addr;
index 72593e1..8866bfc 100644 (file)
@@ -155,6 +155,7 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
 void ip_list_rcv(struct list_head *head, struct packet_type *pt,
                 struct net_device *orig_dev);
 int ip_local_deliver(struct sk_buff *skb);
+void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int proto);
 int ip_mr_input(struct sk_buff *skb);
 int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb);
@@ -421,7 +422,8 @@ static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
 }
 
 struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
-                                       int fc_mx_len);
+                                       int fc_mx_len,
+                                       struct netlink_ext_ack *extack);
 static inline void ip_fib_metrics_put(struct dst_metrics *fib_metrics)
 {
        if (fib_metrics != &dst_default_metrics &&
index 236e40b..69b4bcf 100644 (file)
@@ -69,6 +69,8 @@ struct ip6_tnl_encap_ops {
        size_t (*encap_hlen)(struct ip_tunnel_encap *e);
        int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e,
                            u8 *protocol, struct flowi6 *fl6);
+       int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                          u8 type, u8 code, int offset, __be32 info);
 };
 
 #ifdef CONFIG_INET
index b0d022f..db6b221 100644 (file)
@@ -311,6 +311,7 @@ struct ip_tunnel_encap_ops {
        size_t (*encap_hlen)(struct ip_tunnel_encap *e);
        int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e,
                            u8 *protocol, struct flowi4 *fl4);
+       int (*err_handler)(struct sk_buff *skb, u32 info);
 };
 
 #define MAX_IPTUN_ENCAP_OPS 8
index 8296505..daf8086 100644 (file)
@@ -975,6 +975,8 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb);
 int ip6_forward(struct sk_buff *skb);
 int ip6_input(struct sk_buff *skb);
 int ip6_mc_input(struct sk_buff *skb);
+void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
+                             bool have_final);
 
 int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
 int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
index 3832099..78fa0ac 100644 (file)
@@ -101,6 +101,17 @@ struct net_device *l3mdev_master_dev_rcu(const struct net_device *_dev)
        return master;
 }
 
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex);
+static inline
+int l3mdev_master_upper_ifindex_by_index(struct net *net, int ifindex)
+{
+       rcu_read_lock();
+       ifindex = l3mdev_master_upper_ifindex_by_index_rcu(net, ifindex);
+       rcu_read_unlock();
+
+       return ifindex;
+}
+
 u32 l3mdev_fib_table_rcu(const struct net_device *dev);
 u32 l3mdev_fib_table_by_index(struct net *net, int ifindex);
 static inline u32 l3mdev_fib_table(const struct net_device *dev)
@@ -208,6 +219,17 @@ static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
 }
 
 static inline
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
+{
+       return 0;
+}
+static inline
+int l3mdev_master_upper_ifindex_by_index(struct net *net, int ifindex)
+{
+       return 0;
+}
+
+static inline
 struct net_device *l3mdev_master_dev_rcu(const struct net_device *dev)
 {
        return NULL;
index 665990c..f886b58 100644 (file)
@@ -154,6 +154,7 @@ struct neighbour {
        struct hh_cache         hh;
        int                     (*output)(struct neighbour *, struct sk_buff *);
        const struct neigh_ops  *ops;
+       struct list_head        gc_list;
        struct rcu_head         rcu;
        struct net_device       *dev;
        u8                      primary_key[0];
@@ -214,6 +215,8 @@ struct neigh_table {
        struct timer_list       proxy_timer;
        struct sk_buff_head     proxy_queue;
        atomic_t                entries;
+       atomic_t                gc_entries;
+       struct list_head        gc_list;
        rwlock_t                lock;
        unsigned long           last_rand;
        struct neigh_statistics __percpu *stats;
index e47503b..104a666 100644 (file)
@@ -103,6 +103,9 @@ struct netns_ipv4 {
        /* Shall we try to damage output packets if routing dev changes? */
        int sysctl_ip_dynaddr;
        int sysctl_ip_early_demux;
+#ifdef CONFIG_NET_L3_MASTER_DEV
+       int sysctl_raw_l3mdev_accept;
+#endif
        int sysctl_tcp_early_demux;
        int sysctl_udp_early_demux;
 
index 72ffb31..ea191d8 100644 (file)
@@ -81,6 +81,14 @@ void __tcf_block_cb_unregister(struct tcf_block *block,
                               struct tcf_block_cb *block_cb);
 void tcf_block_cb_unregister(struct tcf_block *block,
                             tc_setup_cb_t *cb, void *cb_ident);
+int __tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                               tc_indr_block_bind_cb_t *cb, void *cb_ident);
+int tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                             tc_indr_block_bind_cb_t *cb, void *cb_ident);
+void __tc_indr_block_cb_unregister(struct net_device *dev,
+                                  tc_indr_block_bind_cb_t *cb, void *cb_ident);
+void tc_indr_block_cb_unregister(struct net_device *dev,
+                                tc_indr_block_bind_cb_t *cb, void *cb_ident);
 
 int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                 struct tcf_result *res, bool compat_mode);
@@ -183,6 +191,32 @@ void tcf_block_cb_unregister(struct tcf_block *block,
 {
 }
 
+static inline
+int __tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                               tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       return 0;
+}
+
+static inline
+int tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                             tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       return 0;
+}
+
+static inline
+void __tc_indr_block_cb_unregister(struct net_device *dev,
+                                  tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+}
+
+static inline
+void tc_indr_block_cb_unregister(struct net_device *dev,
+                                tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+}
+
 static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                               struct tcf_result *res, bool compat_mode)
 {
@@ -609,6 +643,7 @@ struct tc_cls_common_offload {
 
 struct tc_cls_u32_knode {
        struct tcf_exts *exts;
+       struct tcf_result *res;
        struct tc_u32_sel *sel;
        u32 handle;
        u32 val;
@@ -787,12 +822,21 @@ enum tc_mq_command {
        TC_MQ_CREATE,
        TC_MQ_DESTROY,
        TC_MQ_STATS,
+       TC_MQ_GRAFT,
+};
+
+struct tc_mq_opt_offload_graft_params {
+       unsigned long queue;
+       u32 child_handle;
 };
 
 struct tc_mq_qopt_offload {
        enum tc_mq_command command;
        u32 handle;
-       struct tc_qopt_offload_stats stats;
+       union {
+               struct tc_qopt_offload_stats stats;
+               struct tc_mq_opt_offload_graft_params graft_params;
+       };
 };
 
 enum tc_red_command {
@@ -800,13 +844,16 @@ enum tc_red_command {
        TC_RED_DESTROY,
        TC_RED_STATS,
        TC_RED_XSTATS,
+       TC_RED_GRAFT,
 };
 
 struct tc_red_qopt_offload_params {
        u32 min;
        u32 max;
        u32 probability;
+       u32 limit;
        bool is_ecn;
+       bool is_harddrop;
        struct gnet_stats_queue *qstats;
 };
 
@@ -818,6 +865,51 @@ struct tc_red_qopt_offload {
                struct tc_red_qopt_offload_params set;
                struct tc_qopt_offload_stats stats;
                struct red_stats *xstats;
+               u32 child_handle;
+       };
+};
+
+enum tc_gred_command {
+       TC_GRED_REPLACE,
+       TC_GRED_DESTROY,
+       TC_GRED_STATS,
+};
+
+struct tc_gred_vq_qopt_offload_params {
+       bool present;
+       u32 limit;
+       u32 prio;
+       u32 min;
+       u32 max;
+       bool is_ecn;
+       bool is_harddrop;
+       u32 probability;
+       /* Only need backlog, see struct tc_prio_qopt_offload_params */
+       u32 *backlog;
+};
+
+struct tc_gred_qopt_offload_params {
+       bool grio_on;
+       bool wred_on;
+       unsigned int dp_cnt;
+       unsigned int dp_def;
+       struct gnet_stats_queue *qstats;
+       struct tc_gred_vq_qopt_offload_params tab[MAX_DPs];
+};
+
+struct tc_gred_qopt_offload_stats {
+       struct gnet_stats_basic_packed bstats[MAX_DPs];
+       struct gnet_stats_queue qstats[MAX_DPs];
+       struct red_stats *xstats[MAX_DPs];
+};
+
+struct tc_gred_qopt_offload {
+       enum tc_gred_command command;
+       u32 handle;
+       u32 parent;
+       union {
+               struct tc_gred_qopt_offload_params set;
+               struct tc_gred_qopt_offload_stats stats;
        };
 };
 
@@ -854,4 +946,14 @@ struct tc_prio_qopt_offload {
        };
 };
 
+enum tc_root_command {
+       TC_ROOT_GRAFT,
+};
+
+struct tc_root_qopt_offload {
+       enum tc_root_command command;
+       u32 handle;
+       bool ingress;
+};
+
 #endif
index 4fc75f7..92b3eaa 100644 (file)
@@ -42,7 +42,10 @@ struct net_protocol {
        int                     (*early_demux)(struct sk_buff *skb);
        int                     (*early_demux_handler)(struct sk_buff *skb);
        int                     (*handler)(struct sk_buff *skb);
-       void                    (*err_handler)(struct sk_buff *skb, u32 info);
+
+       /* This returns an error if we weren't able to handle the error. */
+       int                     (*err_handler)(struct sk_buff *skb, u32 info);
+
        unsigned int            no_policy:1,
                                netns_ok:1,
                                /* does the protocol do more stringent
@@ -58,10 +61,12 @@ struct inet6_protocol {
        void    (*early_demux_handler)(struct sk_buff *skb);
        int     (*handler)(struct sk_buff *skb);
 
-       void    (*err_handler)(struct sk_buff *skb,
+       /* This returns an error if we weren't able to handle the error. */
+       int     (*err_handler)(struct sk_buff *skb,
                               struct inet6_skb_parm *opt,
                               u8 type, u8 code, int offset,
                               __be32 info);
+
        unsigned int    flags;  /* INET6_PROTO_xxx */
 };
 
index 9c9fa98..821ff48 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef _RAW_H
 #define _RAW_H
 
-
+#include <net/inet_sock.h>
 #include <net/protocol.h>
 #include <linux/icmp.h>
 
@@ -61,6 +61,7 @@ void raw_seq_stop(struct seq_file *seq, void *v);
 
 int raw_hash_sk(struct sock *sk);
 void raw_unhash_sk(struct sock *sk);
+void raw_init(void);
 
 struct raw_sock {
        /* inet_sock has to be the first member */
@@ -74,4 +75,15 @@ static inline struct raw_sock *raw_sk(const struct sock *sk)
        return (struct raw_sock *)sk;
 }
 
+static inline bool raw_sk_bound_dev_eq(struct net *net, int bound_dev_if,
+                                      int dif, int sdif)
+{
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+       return inet_bound_dev_eq(!!net->ipv4.sysctl_raw_l3mdev_accept,
+                                bound_dev_if, dif, sdif);
+#else
+       return inet_bound_dev_eq(true, bound_dev_if, dif, sdif);
+#endif
+}
+
 #endif /* _RAW_H */
index cf26e5a..e2091bb 100644 (file)
@@ -159,7 +159,8 @@ struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
 struct net_device *rtnl_create_link(struct net *net, const char *ifname,
                                    unsigned char name_assign_type,
                                    const struct rtnl_link_ops *ops,
-                                   struct nlattr *tb[]);
+                                   struct nlattr *tb[],
+                                   struct netlink_ext_ack *extack);
 int rtnl_delete_link(struct net_device *dev);
 int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm);
 
index 4d73642..9481f2c 100644 (file)
@@ -24,6 +24,9 @@ struct bpf_flow_keys;
 typedef int tc_setup_cb_t(enum tc_setup_type type,
                          void *type_data, void *cb_priv);
 
+typedef int tc_indr_block_bind_cb_t(struct net_device *dev, void *cb_priv,
+                                   enum tc_setup_type type, void *type_data);
+
 struct qdisc_rate_table {
        struct tc_ratespec rate;
        u32             data[256];
@@ -579,6 +582,30 @@ void qdisc_put(struct Qdisc *qdisc);
 void qdisc_put_unlocked(struct Qdisc *qdisc);
 void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n,
                               unsigned int len);
+#ifdef CONFIG_NET_SCHED
+int qdisc_offload_dump_helper(struct Qdisc *q, enum tc_setup_type type,
+                             void *type_data);
+void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch,
+                               struct Qdisc *new, struct Qdisc *old,
+                               enum tc_setup_type type, void *type_data,
+                               struct netlink_ext_ack *extack);
+#else
+static inline int
+qdisc_offload_dump_helper(struct Qdisc *q, enum tc_setup_type type,
+                         void *type_data)
+{
+       q->flags &= ~TCQ_F_OFFLOADED;
+       return 0;
+}
+
+static inline void
+qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch,
+                          struct Qdisc *new, struct Qdisc *old,
+                          enum tc_setup_type type, void *type_data,
+                          struct netlink_ext_ack *extack)
+{
+}
+#endif
 struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
                          const struct Qdisc_ops *ops,
                          struct netlink_ext_ack *extack);
index 8dadc74..4588bdc 100644 (file)
@@ -71,7 +71,7 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
                                         SCTP_NUM_AUTH_CHUNK_TYPES)
 
 /* These are the different flavours of event.  */
-enum sctp_event {
+enum sctp_event_type {
        SCTP_EVENT_T_CHUNK = 1,
        SCTP_EVENT_T_TIMEOUT,
        SCTP_EVENT_T_OTHER,
index 2abbc15..1d13ec3 100644 (file)
@@ -151,8 +151,8 @@ int sctp_primitive_RECONF(struct net *net, struct sctp_association *asoc,
  * sctp/input.c
  */
 int sctp_rcv(struct sk_buff *skb);
-void sctp_v4_err(struct sk_buff *skb, u32 info);
-void sctp_hash_endpoint(struct sctp_endpoint *);
+int sctp_v4_err(struct sk_buff *skb, u32 info);
+int sctp_hash_endpoint(struct sctp_endpoint *ep);
 void sctp_unhash_endpoint(struct sctp_endpoint *);
 struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
                             struct sctphdr *, struct sctp_association **,
index 9e3d327..24825a8 100644 (file)
@@ -173,7 +173,7 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire;
 __u8 sctp_get_chunk_type(struct sctp_chunk *chunk);
 const struct sctp_sm_table_entry *sctp_sm_lookup_event(
                                        struct net *net,
-                                       enum sctp_event event_type,
+                                       enum sctp_event_type event_type,
                                        enum sctp_state state,
                                        union sctp_subtype event_subtype);
 int sctp_chunk_iif(const struct sctp_chunk *);
@@ -313,7 +313,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
 
 /* Prototypes for statetable processing. */
 
-int sctp_do_sm(struct net *net, enum sctp_event event_type,
+int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
               union sctp_subtype subtype, enum sctp_state state,
               struct sctp_endpoint *ep, struct sctp_association *asoc,
               void *event_arg, gfp_t gfp);
index feada35..003020e 100644 (file)
@@ -96,7 +96,9 @@ struct sctp_stream;
 
 struct sctp_bind_bucket {
        unsigned short  port;
-       unsigned short  fastreuse;
+       signed char     fastreuse;
+       signed char     fastreuseport;
+       kuid_t          fastuid;
        struct hlist_node       node;
        struct hlist_head       owner;
        struct net      *net;
@@ -215,7 +217,7 @@ struct sctp_sock {
         * These two structures must be grouped together for the usercopy
         * whitelist region.
         */
-       struct sctp_event_subscribe subscribe;
+       __u16 subscribe;
        struct sctp_initmsg initmsg;
 
        int user_frag;
@@ -1190,6 +1192,8 @@ int sctp_bind_addr_conflict(struct sctp_bind_addr *, const union sctp_addr *,
                         struct sctp_sock *, struct sctp_sock *);
 int sctp_bind_addr_state(const struct sctp_bind_addr *bp,
                         const union sctp_addr *addr);
+int sctp_bind_addrs_check(struct sctp_sock *sp,
+                         struct sctp_sock *sp2, int cnt2);
 union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr  *bp,
                                        const union sctp_addr   *addrs,
                                        int                     addrcnt,
@@ -2073,6 +2077,8 @@ struct sctp_association {
 
        int sent_cnt_removable;
 
+       __u16 subscribe;
+
        __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
        __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
 
index 51b4e06..bd922a0 100644 (file)
@@ -164,30 +164,39 @@ void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event,
 
 __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event);
 
+static inline void sctp_ulpevent_type_set(__u16 *subscribe,
+                                         __u16 sn_type, __u8 on)
+{
+       if (sn_type > SCTP_SN_TYPE_MAX)
+               return;
+
+       if (on)
+               *subscribe |=  (1 << (sn_type - SCTP_SN_TYPE_BASE));
+       else
+               *subscribe &= ~(1 << (sn_type - SCTP_SN_TYPE_BASE));
+}
+
 /* Is this event type enabled? */
-static inline int sctp_ulpevent_type_enabled(__u16 sn_type,
-                                            struct sctp_event_subscribe *mask)
+static inline bool sctp_ulpevent_type_enabled(__u16 subscribe, __u16 sn_type)
 {
-       int offset = sn_type - SCTP_SN_TYPE_BASE;
-       char *amask = (char *) mask;
+       if (sn_type > SCTP_SN_TYPE_MAX)
+               return false;
 
-       if (offset >= sizeof(struct sctp_event_subscribe))
-               return 0;
-       return amask[offset];
+       return subscribe & (1 << (sn_type - SCTP_SN_TYPE_BASE));
 }
 
 /* Given an event subscription, is this event enabled? */
-static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
-                                          struct sctp_event_subscribe *mask)
+static inline bool sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event,
+                                           __u16 subscribe)
 {
        __u16 sn_type;
-       int enabled = 1;
 
-       if (sctp_ulpevent_is_notification(event)) {
-               sn_type = sctp_ulpevent_get_notification_type(event);
-               enabled = sctp_ulpevent_type_enabled(sn_type, mask);
-       }
-       return enabled;
+       if (!sctp_ulpevent_is_notification(event))
+               return true;
+
+       sn_type = sctp_ulpevent_get_notification_type(event);
+
+       return sctp_ulpevent_type_enabled(subscribe, sn_type);
 }
 
 #endif /* __sctp_ulpevent_h__ */
index f665d74..df390a3 100644 (file)
@@ -1110,7 +1110,7 @@ struct proto {
        unsigned int            inuse_idx;
 #endif
 
-       bool                    (*stream_memory_free)(const struct sock *sk);
+       bool                    (*stream_memory_free)(const struct sock *sk, int wake);
        bool                    (*stream_memory_read)(const struct sock *sk);
        /* Memory pressure */
        void                    (*enter_memory_pressure)(struct sock *sk);
@@ -1192,19 +1192,29 @@ static inline void sk_refcnt_debug_release(const struct sock *sk)
 #define sk_refcnt_debug_release(sk) do { } while (0)
 #endif /* SOCK_REFCNT_DEBUG */
 
-static inline bool sk_stream_memory_free(const struct sock *sk)
+static inline bool __sk_stream_memory_free(const struct sock *sk, int wake)
 {
        if (sk->sk_wmem_queued >= sk->sk_sndbuf)
                return false;
 
        return sk->sk_prot->stream_memory_free ?
-               sk->sk_prot->stream_memory_free(sk) : true;
+               sk->sk_prot->stream_memory_free(sk, wake) : true;
 }
 
-static inline bool sk_stream_is_writeable(const struct sock *sk)
+static inline bool sk_stream_memory_free(const struct sock *sk)
+{
+       return __sk_stream_memory_free(sk, 0);
+}
+
+static inline bool __sk_stream_is_writeable(const struct sock *sk, int wake)
 {
        return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) &&
-              sk_stream_memory_free(sk);
+              __sk_stream_memory_free(sk, wake);
+}
+
+static inline bool sk_stream_is_writeable(const struct sock *sk)
+{
+       return __sk_stream_is_writeable(sk, 0);
 }
 
 static inline int sk_under_cgroup_hierarchy(struct sock *sk,
index 881ecb1..866b6d1 100644 (file)
@@ -95,8 +95,8 @@ struct switchdev_obj_port_vlan {
        u16 vid_end;
 };
 
-#define SWITCHDEV_OBJ_PORT_VLAN(obj) \
-       container_of(obj, struct switchdev_obj_port_vlan, obj)
+#define SWITCHDEV_OBJ_PORT_VLAN(OBJ) \
+       container_of((OBJ), struct switchdev_obj_port_vlan, obj)
 
 /* SWITCHDEV_OBJ_ID_PORT_MDB */
 struct switchdev_obj_port_mdb {
@@ -105,8 +105,8 @@ struct switchdev_obj_port_mdb {
        u16 vid;
 };
 
-#define SWITCHDEV_OBJ_PORT_MDB(obj) \
-       container_of(obj, struct switchdev_obj_port_mdb, obj)
+#define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
+       container_of((OBJ), struct switchdev_obj_port_mdb, obj)
 
 void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
                                  void *data, void (*destructor)(void const *),
@@ -121,10 +121,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
  * @switchdev_port_attr_get: Get a port attribute (see switchdev_attr).
  *
  * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
- *
- * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*).
- *
- * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*).
  */
 struct switchdev_ops {
        int     (*switchdev_port_attr_get)(struct net_device *dev,
@@ -132,11 +128,6 @@ struct switchdev_ops {
        int     (*switchdev_port_attr_set)(struct net_device *dev,
                                           const struct switchdev_attr *attr,
                                           struct switchdev_trans *trans);
-       int     (*switchdev_port_obj_add)(struct net_device *dev,
-                                         const struct switchdev_obj *obj,
-                                         struct switchdev_trans *trans);
-       int     (*switchdev_port_obj_del)(struct net_device *dev,
-                                         const struct switchdev_obj *obj);
 };
 
 enum switchdev_notifier_type {
@@ -146,6 +137,11 @@ enum switchdev_notifier_type {
        SWITCHDEV_FDB_DEL_TO_DEVICE,
        SWITCHDEV_FDB_OFFLOADED,
 
+       SWITCHDEV_PORT_OBJ_ADD, /* Blocking. */
+       SWITCHDEV_PORT_OBJ_DEL, /* Blocking. */
+
+       SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE,
+       SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE,
        SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE,
        SWITCHDEV_VXLAN_FDB_OFFLOADED,
@@ -163,6 +159,13 @@ struct switchdev_notifier_fdb_info {
           offloaded:1;
 };
 
+struct switchdev_notifier_port_obj_info {
+       struct switchdev_notifier_info info; /* must be first */
+       const struct switchdev_obj *obj;
+       struct switchdev_trans *trans;
+       bool handled;
+};
+
 static inline struct net_device *
 switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
 {
@@ -180,10 +183,17 @@ int switchdev_port_obj_add(struct net_device *dev,
                           const struct switchdev_obj *obj);
 int switchdev_port_obj_del(struct net_device *dev,
                           const struct switchdev_obj *obj);
+
 int register_switchdev_notifier(struct notifier_block *nb);
 int unregister_switchdev_notifier(struct notifier_block *nb);
 int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
                             struct switchdev_notifier_info *info);
+
+int register_switchdev_blocking_notifier(struct notifier_block *nb);
+int unregister_switchdev_blocking_notifier(struct notifier_block *nb);
+int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
+                                     struct switchdev_notifier_info *info);
+
 void switchdev_port_fwd_mark_set(struct net_device *dev,
                                 struct net_device *group_dev,
                                 bool joining);
@@ -191,6 +201,18 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
 bool switchdev_port_same_parent_id(struct net_device *a,
                                   struct net_device *b);
 
+int switchdev_handle_port_obj_add(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans));
+int switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj));
+
 #define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops))
 #else
 
@@ -239,12 +261,53 @@ static inline int call_switchdev_notifiers(unsigned long val,
        return NOTIFY_DONE;
 }
 
+static inline int
+register_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       return 0;
+}
+
+static inline int
+unregister_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       return 0;
+}
+
+static inline int
+call_switchdev_blocking_notifiers(unsigned long val,
+                                 struct net_device *dev,
+                                 struct switchdev_notifier_info *info)
+{
+       return NOTIFY_DONE;
+}
+
 static inline bool switchdev_port_same_parent_id(struct net_device *a,
                                                 struct net_device *b)
 {
        return false;
 }
 
+static inline int
+switchdev_handle_port_obj_add(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans))
+{
+       return 0;
+}
+
+static inline int
+switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       return 0;
+}
+
 #define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0)
 
 #endif
index a18914d..e0a65c0 100644 (file)
@@ -313,7 +313,7 @@ extern struct proto tcp_prot;
 
 void tcp_tasklet_init(void);
 
-void tcp_v4_err(struct sk_buff *skb, u32);
+int tcp_v4_err(struct sk_buff *skb, u32);
 
 void tcp_shutdown(struct sock *sk, int how);
 
@@ -1124,7 +1124,7 @@ void tcp_rate_check_app_limited(struct sock *sk);
  */
 static inline int tcp_is_sack(const struct tcp_sock *tp)
 {
-       return tp->rx_opt.sack_ok;
+       return likely(tp->rx_opt.sack_ok);
 }
 
 static inline bool tcp_is_reno(const struct tcp_sock *tp)
@@ -1315,33 +1315,16 @@ static inline __sum16 tcp_v4_check(int len, __be32 saddr,
        return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
 }
 
-static inline __sum16 __tcp_checksum_complete(struct sk_buff *skb)
-{
-       return __skb_checksum_complete(skb);
-}
-
 static inline bool tcp_checksum_complete(struct sk_buff *skb)
 {
        return !skb_csum_unnecessary(skb) &&
-               __tcp_checksum_complete(skb);
+               __skb_checksum_complete(skb);
 }
 
 bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb);
 int tcp_filter(struct sock *sk, struct sk_buff *skb);
-
-#undef STATE_TRACE
-
-#ifdef STATE_TRACE
-static const char *statename[]={
-       "Unused","Established","Syn Sent","Syn Recv",
-       "Fin Wait 1","Fin Wait 2","Time Wait", "Close",
-       "Close Wait","Last ACK","Listen","Closing"
-};
-#endif
 void tcp_set_state(struct sock *sk, int state);
-
 void tcp_done(struct sock *sk);
-
 int tcp_abort(struct sock *sk, int err);
 
 static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
@@ -1385,7 +1368,7 @@ static inline int tcp_win_from_space(const struct sock *sk, int space)
 /* Note: caller must be prepared to deal with negative returns */
 static inline int tcp_space(const struct sock *sk)
 {
-       return tcp_win_from_space(sk, sk->sk_rcvbuf -
+       return tcp_win_from_space(sk, sk->sk_rcvbuf - sk->sk_backlog.len -
                                  atomic_read(&sk->sk_rmem_alloc));
 }
 
@@ -1572,9 +1555,21 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
                                         const struct sock *addr_sk);
 
 #ifdef CONFIG_TCP_MD5SIG
-struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
-                                        const union tcp_md5_addr *addr,
-                                        int family);
+#include <linux/jump_label.h>
+extern struct static_key tcp_md5_needed;
+struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk,
+                                          const union tcp_md5_addr *addr,
+                                          int family);
+static inline struct tcp_md5sig_key *
+tcp_md5_do_lookup(const struct sock *sk,
+                 const union tcp_md5_addr *addr,
+                 int family)
+{
+       if (!static_key_false(&tcp_md5_needed))
+               return NULL;
+       return __tcp_md5_do_lookup(sk, addr, family);
+}
+
 #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key)
 #else
 static inline struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
@@ -1875,12 +1870,16 @@ static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
        return tp->notsent_lowat ?: net->ipv4.sysctl_tcp_notsent_lowat;
 }
 
-static inline bool tcp_stream_memory_free(const struct sock *sk)
+/* @wake is one when sk_stream_write_space() calls us.
+ * This sends EPOLLOUT only if notsent_bytes is half the limit.
+ * This mimics the strategy used in sock_def_write_space().
+ */
+static inline bool tcp_stream_memory_free(const struct sock *sk, int wake)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        u32 notsent_bytes = tp->write_seq - tp->snd_nxt;
 
-       return notsent_bytes < tcp_notsent_lowat(tp);
+       return (notsent_bytes << wake) < tcp_notsent_lowat(tp);
 }
 
 #ifdef CONFIG_PROC_FS
index 9e82cb3..fd6d948 100644 (file)
@@ -252,6 +252,17 @@ static inline int udp_rqueue_get(struct sock *sk)
        return sk_rmem_alloc_get(sk) - READ_ONCE(udp_sk(sk)->forward_deficit);
 }
 
+static inline bool udp_sk_bound_dev_eq(struct net *net, int bound_dev_if,
+                                      int dif, int sdif)
+{
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+       return inet_bound_dev_eq(!!net->ipv4.sysctl_udp_l3mdev_accept,
+                                bound_dev_if, dif, sdif);
+#else
+       return inet_bound_dev_eq(true, bound_dev_if, dif, sdif);
+#endif
+}
+
 /* net/ipv4/udp.c */
 void udp_destruct_sock(struct sock *sk);
 void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len);
@@ -272,7 +283,7 @@ bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
 int udp_get_port(struct sock *sk, unsigned short snum,
                 int (*saddr_cmp)(const struct sock *,
                                  const struct sock *));
-void udp_err(struct sk_buff *, u32);
+int udp_err(struct sk_buff *, u32);
 int udp_abort(struct sock *sk, int err);
 int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
 int udp_push_pending_frames(struct sock *sk);
@@ -406,17 +417,24 @@ static inline int copy_linear_skb(struct sk_buff *skb, int len, int off,
 } while(0)
 
 #if IS_ENABLED(CONFIG_IPV6)
-#define __UDPX_INC_STATS(sk, field)                                    \
-do {                                                                   \
-       if ((sk)->sk_family == AF_INET)                                 \
-               __UDP_INC_STATS(sock_net(sk), field, 0);                \
-       else                                                            \
-               __UDP6_INC_STATS(sock_net(sk), field, 0);               \
-} while (0)
+#define __UDPX_MIB(sk, ipv4)                                           \
+({                                                                     \
+       ipv4 ? (IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_statistics : \
+                                sock_net(sk)->mib.udp_statistics) :    \
+               (IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_stats_in6 : \
+                                sock_net(sk)->mib.udp_stats_in6);      \
+})
 #else
-#define __UDPX_INC_STATS(sk, field) __UDP_INC_STATS(sock_net(sk), field, 0)
+#define __UDPX_MIB(sk, ipv4)                                           \
+({                                                                     \
+       IS_UDPLITE(sk) ? sock_net(sk)->mib.udplite_statistics :         \
+                        sock_net(sk)->mib.udp_statistics;              \
+})
 #endif
 
+#define __UDPX_INC_STATS(sk, field) \
+       __SNMP_INC_STATS(__UDPX_MIB(sk, (sk)->sk_family == AF_INET), field)
+
 #ifdef CONFIG_PROC_FS
 struct udp_seq_afinfo {
        sa_family_t                     family;
@@ -450,4 +468,26 @@ DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
 void udpv6_encap_enable(void);
 #endif
 
+static inline struct sk_buff *udp_rcv_segment(struct sock *sk,
+                                             struct sk_buff *skb, bool ipv4)
+{
+       struct sk_buff *segs;
+
+       /* the GSO CB lays after the UDP one, no need to save and restore any
+        * CB fragment
+        */
+       segs = __skb_gso_segment(skb, NETIF_F_SG, false);
+       if (unlikely(IS_ERR_OR_NULL(segs))) {
+               int segs_nr = skb_shinfo(skb)->gso_segs;
+
+               atomic_add(segs_nr, &sk->sk_drops);
+               SNMP_ADD_STATS(__UDPX_MIB(sk, ipv4), UDP_MIB_INERRORS, segs_nr);
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       consume_skb(skb);
+       return segs;
+}
+
 #endif /* _UDP_H */
index fe680ab..b813795 100644 (file)
@@ -30,6 +30,7 @@ struct udp_port_cfg {
 
        __be16                  local_udp_port;
        __be16                  peer_udp_port;
+       int                     bind_ifindex;
        unsigned int            use_udp_checksums:1,
                                use_udp6_tx_checksums:1,
                                use_udp6_rx_checksums:1,
@@ -64,6 +65,8 @@ static inline int udp_sock_create(struct net *net,
 }
 
 typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb);
+typedef int (*udp_tunnel_encap_err_lookup_t)(struct sock *sk,
+                                            struct sk_buff *skb);
 typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk);
 typedef struct sk_buff *(*udp_tunnel_gro_receive_t)(struct sock *sk,
                                                    struct list_head *head,
@@ -76,6 +79,7 @@ struct udp_tunnel_sock_cfg {
        /* Used for setting up udp_sock fields, see udp.h for details */
        __u8  encap_type;
        udp_tunnel_encap_rcv_t encap_rcv;
+       udp_tunnel_encap_err_lookup_t encap_err_lookup;
        udp_tunnel_encap_destroy_t encap_destroy;
        udp_tunnel_gro_receive_t gro_receive;
        udp_tunnel_gro_complete_t gro_complete;
@@ -165,6 +169,12 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)
 
 static inline void udp_tunnel_encap_enable(struct socket *sock)
 {
+       struct udp_sock *up = udp_sk(sock->sk);
+
+       if (up->encap_enabled)
+               return;
+
+       up->encap_enabled = 1;
 #if IS_ENABLED(CONFIG_IPV6)
        if (sock->sk->sk_family == PF_INET6)
                ipv6_stub->udpv6_encap_enable();
index 03431c1..236403e 100644 (file)
@@ -216,6 +216,7 @@ struct vxlan_config {
        unsigned long           age_interval;
        unsigned int            addrmax;
        bool                    no_share;
+       enum ifla_vxlan_df      df;
 };
 
 struct vxlan_dev_node {
@@ -420,11 +421,16 @@ struct switchdev_notifier_vxlan_fdb_info {
        u8 eth_addr[ETH_ALEN];
        __be32 vni;
        bool offloaded;
+       bool added_by_user;
 };
 
 #if IS_ENABLED(CONFIG_VXLAN)
 int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
                      struct switchdev_notifier_vxlan_fdb_info *fdb_info);
+int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                    struct notifier_block *nb);
+void vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni);
+
 #else
 static inline int
 vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
@@ -432,6 +438,17 @@ vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
 {
        return -ENOENT;
 }
+
+static inline int vxlan_fdb_replay(const struct net_device *dev, __be32 vni,
+                                  struct notifier_block *nb)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void
+vxlan_fdb_clear_offload(const struct net_device *dev, __be32 vni)
+{
+}
 #endif
 
 #endif
index 5687766..5cc7af0 100644 (file)
@@ -1205,8 +1205,10 @@ void qman_dqrr_get_ithresh(struct qman_portal *portal, u8 *ithresh);
  * qman_dqrr_set_ithresh - Set coalesce interrupt threshold
  * @portal: portal to set the new value on
  * @ithresh: new threshold value
+ *
+ * Returns 0 on success, or a negative error code.
  */
-void qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh);
+int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh);
 
 /**
  * qman_dqrr_get_iperiod - Get coalesce interrupt period
@@ -1219,7 +1221,9 @@ void qman_portal_get_iperiod(struct qman_portal *portal, u32 *iperiod);
  * qman_dqrr_set_iperiod - Set coalesce interrupt period
  * @portal: portal to set the new value on
  * @ithresh: new period value
+ *
+ * Returns 0 on success, or a negative error code.
  */
-void qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod);
+int qman_portal_set_iperiod(struct qman_portal *portal, u32 iperiod);
 
 #endif /* __FSL_QMAN_H */
index 00aa72c..1efd7d9 100644 (file)
@@ -244,6 +244,65 @@ DEFINE_EVENT(net_dev_rx_verbose_template, netif_rx_ni_entry,
        TP_ARGS(skb)
 );
 
+DECLARE_EVENT_CLASS(net_dev_rx_exit_template,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret),
+
+       TP_STRUCT__entry(
+               __field(int,    ret)
+       ),
+
+       TP_fast_assign(
+               __entry->ret = ret;
+       ),
+
+       TP_printk("ret=%d", __entry->ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, napi_gro_frags_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, napi_gro_receive_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_rx_ni_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
+DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_list_exit,
+
+       TP_PROTO(int ret),
+
+       TP_ARGS(ret)
+);
+
 #endif /* _TRACE_NET_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/objagg.h b/include/trace/events/objagg.h
new file mode 100644 (file)
index 0000000..fcec0fc
--- /dev/null
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM objagg
+
+#if !defined(__TRACE_OBJAGG_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __TRACE_OBJAGG_H
+
+#include <linux/tracepoint.h>
+
+struct objagg;
+struct objagg_obj;
+
+TRACE_EVENT(objagg_create,
+       TP_PROTO(const struct objagg *objagg),
+
+       TP_ARGS(objagg),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+       ),
+
+       TP_printk("objagg %p", __entry->objagg)
+);
+
+TRACE_EVENT(objagg_destroy,
+       TP_PROTO(const struct objagg *objagg),
+
+       TP_ARGS(objagg),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+       ),
+
+       TP_printk("objagg %p", __entry->objagg)
+);
+
+TRACE_EVENT(objagg_obj_create,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_destroy,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p", __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_get,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                unsigned int refcount),
+
+       TP_ARGS(objagg, obj, refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(unsigned int, refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->refcount = refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, refcount %u",
+                 __entry->objagg, __entry->obj, __entry->refcount)
+);
+
+TRACE_EVENT(objagg_obj_put,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                unsigned int refcount),
+
+       TP_ARGS(objagg, obj, refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(unsigned int, refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->refcount = refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, refcount %u",
+                 __entry->objagg, __entry->obj, __entry->refcount)
+);
+
+TRACE_EVENT(objagg_obj_parent_assign,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                const struct objagg_obj *parent,
+                unsigned int parent_refcount),
+
+       TP_ARGS(objagg, obj, parent, parent_refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(const void *, parent)
+               __field(unsigned int, parent_refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->parent = parent;
+               __entry->parent_refcount = parent_refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u",
+                 __entry->objagg, __entry->obj,
+                 __entry->parent, __entry->parent_refcount)
+);
+
+TRACE_EVENT(objagg_obj_parent_unassign,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj,
+                const struct objagg_obj *parent,
+                unsigned int parent_refcount),
+
+       TP_ARGS(objagg, obj, parent, parent_refcount),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+               __field(const void *, parent)
+               __field(unsigned int, parent_refcount)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+               __entry->parent = parent;
+               __entry->parent_refcount = parent_refcount;
+       ),
+
+       TP_printk("objagg %p, obj %p, parent %p, parent_refcount %u",
+                 __entry->objagg, __entry->obj,
+                 __entry->parent, __entry->parent_refcount)
+);
+
+TRACE_EVENT(objagg_obj_root_create,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p",
+                 __entry->objagg, __entry->obj)
+);
+
+TRACE_EVENT(objagg_obj_root_destroy,
+       TP_PROTO(const struct objagg *objagg,
+                const struct objagg_obj *obj),
+
+       TP_ARGS(objagg, obj),
+
+       TP_STRUCT__entry(
+               __field(const void *, objagg)
+               __field(const void *, obj)
+       ),
+
+       TP_fast_assign(
+               __entry->objagg = objagg;
+               __entry->obj = obj;
+       ),
+
+       TP_printk("objagg %p, obj %p",
+                 __entry->objagg, __entry->obj)
+);
+
+#endif /* __TRACE_OBJAGG_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 72c453a..ec8b40f 100644 (file)
@@ -257,9 +257,6 @@ enum bpf_attach_type {
 /* Specify numa node during map creation */
 #define BPF_F_NUMA_NODE                (1U << 2)
 
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
-
 #define BPF_OBJ_NAME_LEN 16U
 
 /* Flags for accessing BPF object */
@@ -269,6 +266,12 @@ enum bpf_attach_type {
 /* Flag for stack_map, store build_id+offset instead of pointer */
 #define BPF_F_STACK_BUILD_ID   (1U << 5)
 
+/* Zero-initialize hash function seed. This should only be used for testing. */
+#define BPF_F_ZERO_SEED                (1U << 6)
+
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -335,6 +338,10 @@ union bpf_attr {
                 * (context accesses, allowed helpers, etc).
                 */
                __u32           expected_attach_type;
+               __u32           prog_btf_fd;    /* fd pointing to BTF type data */
+               __u32           func_info_rec_size;     /* userspace bpf_func_info size */
+               __aligned_u64   func_info;      /* func info */
+               __u32           func_info_cnt;  /* number of bpf_func_info records */
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -2265,6 +2272,19 @@ union bpf_attr {
  *
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
+ *      Description
+ *             Will remove *pop* bytes from a *msg* starting at byte *start*.
+ *             This may result in **ENOMEM** errors under certain situations if
+ *             an allocation and copy are required due to a full ring buffer.
+ *             However, the helper will try to avoid doing the allocation
+ *             if possible. Other errors can occur if input parameters are
+ *             invalid either due to *start* byte not being valid part of msg
+ *             payload and/or *pop* value being to large.
+ *
+ *     Return
+ *             0 on success, or a negative erro in case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2357,7 +2377,8 @@ union bpf_attr {
        FN(map_push_elem),              \
        FN(map_pop_elem),               \
        FN(map_peek_elem),              \
-       FN(msg_push_data),
+       FN(msg_push_data),              \
+       FN(msg_pop_data),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2474,6 +2495,7 @@ struct __sk_buff {
 
        __u32 data_meta;
        __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
+       __u64 tstamp;
 };
 
 struct bpf_tunnel_key {
@@ -2649,6 +2671,10 @@ struct bpf_prog_info {
        __u32 nr_jited_func_lens;
        __aligned_u64 jited_ksyms;
        __aligned_u64 jited_func_lens;
+       __u32 btf_id;
+       __u32 func_info_rec_size;
+       __aligned_u64 func_info;
+       __u32 func_info_cnt;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -2960,4 +2986,9 @@ struct bpf_flow_keys {
        };
 };
 
+struct bpf_func_info {
+       __u32   insn_offset;
+       __u32   type_id;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 972265f..14f6694 100644 (file)
@@ -40,7 +40,8 @@ struct btf_type {
        /* "size" is used by INT, ENUM, STRUCT and UNION.
         * "size" tells the size of the type it is describing.
         *
-        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC and FUNC_PROTO.
         * "type" is a type_id referring to another type.
         */
        union {
@@ -64,8 +65,10 @@ struct btf_type {
 #define BTF_KIND_VOLATILE      9       /* Volatile     */
 #define BTF_KIND_CONST         10      /* Const        */
 #define BTF_KIND_RESTRICT      11      /* Restrict     */
-#define BTF_KIND_MAX           11
-#define NR_BTF_KINDS           12
+#define BTF_KIND_FUNC          12      /* Function     */
+#define BTF_KIND_FUNC_PROTO    13      /* Function Proto       */
+#define BTF_KIND_MAX           13
+#define NR_BTF_KINDS           14
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -110,4 +113,13 @@ struct btf_member {
        __u32   offset; /* offset in bits */
 };
 
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
index 79407bb..6e52d36 100644 (file)
@@ -163,6 +163,11 @@ enum devlink_param_cmode {
        DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
 };
 
+enum devlink_param_fw_load_policy_value {
+       DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
+       DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
+};
+
 enum devlink_attr {
        /* don't change the order or add anything between, this is ABI! */
        DEVLINK_ATTR_UNSPEC,
index c8f8e24..17be76a 100644 (file)
@@ -882,7 +882,7 @@ struct ethtool_rx_flow_spec {
        __u32           location;
 };
 
-/* How rings are layed out when accessing virtual functions or
+/* How rings are laid out when accessing virtual functions or
  * offloaded queues is device specific. To allow users to do flow
  * steering and specify these queues the ring cookie is partitioned
  * into a 32bit queue index with an 8 bit virtual function id.
@@ -891,7 +891,7 @@ struct ethtool_rx_flow_spec {
  * devices start supporting PCIe w/ARI. However at the moment I
  * do not know of any devices that support this so I do not reserve
  * space for this at this time. If a future patch consumes the next
- * byte it should be aware of this possiblity.
+ * byte it should be aware of this possibility.
  */
 #define ETHTOOL_RX_FLOW_SPEC_RING      0x00000000FFFFFFFFLL
 #define ETHTOOL_RX_FLOW_SPEC_RING_VF   0x000000FF00000000LL
index e41eda3..773e476 100644 (file)
@@ -292,4 +292,25 @@ struct br_mcast_stats {
        __u64 mcast_bytes[BR_MCAST_DIR_SIZE];
        __u64 mcast_packets[BR_MCAST_DIR_SIZE];
 };
+
+/* bridge boolean options
+ * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
+ *
+ * IMPORTANT: if adding a new option do not forget to handle
+ *            it in br_boolopt_toggle/get and bridge sysfs
+ */
+enum br_boolopt_id {
+       BR_BOOLOPT_NO_LL_LEARN,
+       BR_BOOLOPT_MAX
+};
+
+/* struct br_boolopt_multi - change multiple bridge boolean options
+ *
+ * @optval: new option values (bit per option)
+ * @optmask: options to change (bit per option)
+ */
+struct br_boolopt_multi {
+       __u32 optval;
+       __u32 optmask;
+};
 #endif /* _UAPI_LINUX_IF_BRIDGE_H */
index 1debfa4..d653382 100644 (file)
@@ -288,6 +288,7 @@ enum {
        IFLA_BR_MCAST_IGMP_VERSION,
        IFLA_BR_MCAST_MLD_VERSION,
        IFLA_BR_VLAN_STATS_PER_PORT,
+       IFLA_BR_MULTI_BOOLOPT,
        __IFLA_BR_MAX,
 };
 
@@ -533,6 +534,7 @@ enum {
        IFLA_VXLAN_LABEL,
        IFLA_VXLAN_GPE,
        IFLA_VXLAN_TTL_INHERIT,
+       IFLA_VXLAN_DF,
        __IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -542,6 +544,14 @@ struct ifla_vxlan_port_range {
        __be16  high;
 };
 
+enum ifla_vxlan_df {
+       VXLAN_DF_UNSET = 0,
+       VXLAN_DF_SET,
+       VXLAN_DF_INHERIT,
+       __VXLAN_DF_END,
+       VXLAN_DF_MAX = __VXLAN_DF_END - 1,
+};
+
 /* GENEVE section */
 enum {
        IFLA_GENEVE_UNSPEC,
@@ -557,10 +567,19 @@ enum {
        IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
        IFLA_GENEVE_LABEL,
        IFLA_GENEVE_TTL_INHERIT,
+       IFLA_GENEVE_DF,
        __IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX        (__IFLA_GENEVE_MAX - 1)
 
+enum ifla_geneve_df {
+       GENEVE_DF_UNSET = 0,
+       GENEVE_DF_SET,
+       GENEVE_DF_INHERIT,
+       __GENEVE_DF_END,
+       GENEVE_DF_MAX = __GENEVE_DF_END - 1,
+};
+
 /* PPP section */
 enum {
        IFLA_PPP_UNSPEC,
index ee432cd..23a6753 100644 (file)
@@ -59,6 +59,7 @@
 #define TUNGETVNETBE _IOR('T', 223, int)
 #define TUNSETSTEERINGEBPF _IOR('T', 224, int)
 #define TUNSETFILTEREBPF _IOR('T', 225, int)
+#define TUNSETCARRIER _IOW('T', 226, int)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN                0x0001
index 0a26a55..a3f87c5 100644 (file)
  * @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
  *     Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
  *     and NCSI_ATTR_CHANNEL_ID.
+ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages.
+ *     Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK.
+ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels.
+ *     Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and
+ *     NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets
+ *     the primary channel.
  * @NCSI_CMD_MAX: highest command number
  */
 enum ncsi_nl_commands {
@@ -34,6 +40,8 @@ enum ncsi_nl_commands {
        NCSI_CMD_SET_INTERFACE,
        NCSI_CMD_CLEAR_INTERFACE,
        NCSI_CMD_SEND_CMD,
+       NCSI_CMD_SET_PACKAGE_MASK,
+       NCSI_CMD_SET_CHANNEL_MASK,
 
        __NCSI_CMD_AFTER_LAST,
        NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
@@ -48,6 +56,10 @@ enum ncsi_nl_commands {
  * @NCSI_ATTR_PACKAGE_ID: package ID
  * @NCSI_ATTR_CHANNEL_ID: channel ID
  * @NCSI_ATTR_DATA: command payload
+ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with
+ *     NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK.
+ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages.
+ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels.
  * @NCSI_ATTR_MAX: highest attribute number
  */
 enum ncsi_nl_attrs {
@@ -57,6 +69,9 @@ enum ncsi_nl_attrs {
        NCSI_ATTR_PACKAGE_ID,
        NCSI_ATTR_CHANNEL_ID,
        NCSI_ATTR_DATA,
+       NCSI_ATTR_MULTI_FLAG,
+       NCSI_ATTR_PACKAGE_MASK,
+       NCSI_ATTR_CHANNEL_MASK,
 
        __NCSI_ATTR_AFTER_LAST,
        NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
index 0187c74..9f99568 100644 (file)
@@ -16,6 +16,8 @@ enum {
        NETNSA_NSID,
        NETNSA_PID,
        NETNSA_FD,
+       NETNSA_TARGET_NSID,
+       NETNSA_CURRENT_NSID,
        __NETNSA_MAX,
 };
 
index 401d0c1..95d0db2 100644 (file)
@@ -485,6 +485,11 @@ enum {
 
        TCA_FLOWER_IN_HW_COUNT,
 
+       TCA_FLOWER_KEY_PORT_SRC_MIN,    /* be16 */
+       TCA_FLOWER_KEY_PORT_SRC_MAX,    /* be16 */
+       TCA_FLOWER_KEY_PORT_DST_MIN,    /* be16 */
+       TCA_FLOWER_KEY_PORT_DST_MAX,    /* be16 */
+
        __TCA_FLOWER_MAX,
 };
 
@@ -518,6 +523,8 @@ enum {
        TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
 };
 
+#define TCA_FLOWER_MASK_FLAGS_RANGE    (1 << 0) /* Range-based match */
+
 /* Match-all classifier */
 
 enum {
index 89ee47c..0d18b1d 100644 (file)
@@ -291,11 +291,38 @@ enum {
        TCA_GRED_DPS,
        TCA_GRED_MAX_P,
        TCA_GRED_LIMIT,
+       TCA_GRED_VQ_LIST,       /* nested TCA_GRED_VQ_ENTRY */
        __TCA_GRED_MAX,
 };
 
 #define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
 
+enum {
+       TCA_GRED_VQ_ENTRY_UNSPEC,
+       TCA_GRED_VQ_ENTRY,      /* nested TCA_GRED_VQ_* */
+       __TCA_GRED_VQ_ENTRY_MAX,
+};
+#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1)
+
+enum {
+       TCA_GRED_VQ_UNSPEC,
+       TCA_GRED_VQ_PAD,
+       TCA_GRED_VQ_DP,                 /* u32 */
+       TCA_GRED_VQ_STAT_BYTES,         /* u64 */
+       TCA_GRED_VQ_STAT_PACKETS,       /* u32 */
+       TCA_GRED_VQ_STAT_BACKLOG,       /* u32 */
+       TCA_GRED_VQ_STAT_PROB_DROP,     /* u32 */
+       TCA_GRED_VQ_STAT_PROB_MARK,     /* u32 */
+       TCA_GRED_VQ_STAT_FORCED_DROP,   /* u32 */
+       TCA_GRED_VQ_STAT_FORCED_MARK,   /* u32 */
+       TCA_GRED_VQ_STAT_PDROP,         /* u32 */
+       TCA_GRED_VQ_STAT_OTHER,         /* u32 */
+       TCA_GRED_VQ_FLAGS,              /* u32 */
+       __TCA_GRED_VQ_MAX
+};
+
+#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1)
+
 struct tc_gred_qopt {
        __u32           limit;        /* HARD maximal queue length (bytes)    */
        __u32           qth_min;      /* Min average length threshold (bytes) */
@@ -864,6 +891,8 @@ enum {
 
        TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */
 
+       TCA_FQ_CE_THRESHOLD,    /* DCTCP-like CE-marking threshold */
+
        __TCA_FQ_MAX
 };
 
@@ -882,6 +911,7 @@ struct tc_fq_qd_stats {
        __u32   inactive_flows;
        __u32   throttled_flows;
        __u32   unthrottle_latency_ns;
+       __u64   ce_mark;                /* packets above ce_threshold */
 };
 
 /* Heavy-Hitter Filter */
index 3039bf6..d73d839 100644 (file)
@@ -84,6 +84,16 @@ struct ptp_sys_offset {
        struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1];
 };
 
+struct ptp_sys_offset_extended {
+       unsigned int n_samples; /* Desired number of measurements. */
+       unsigned int rsv[3];    /* Reserved for future use. */
+       /*
+        * Array of [system, phc, system] time stamps. The kernel will provide
+        * 3*n_samples time stamps.
+        */
+       struct ptp_clock_time ts[PTP_MAX_SAMPLES][3];
+};
+
 struct ptp_sys_offset_precise {
        struct ptp_clock_time device;
        struct ptp_clock_time sys_realtime;
@@ -136,6 +146,8 @@ struct ptp_pin_desc {
 #define PTP_PIN_SETFUNC    _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc)
 #define PTP_SYS_OFFSET_PRECISE \
        _IOWR(PTP_CLK_MAGIC, 8, struct ptp_sys_offset_precise)
+#define PTP_SYS_OFFSET_EXTENDED \
+       _IOW(PTP_CLK_MAGIC, 9, struct ptp_sys_offset_extended)
 
 struct ptp_extts_event {
        struct ptp_clock_time t; /* Time event occured. */
index c81feb3..d584073 100644 (file)
@@ -129,6 +129,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_STREAM_SCHEDULER_VALUE    124
 #define SCTP_INTERLEAVING_SUPPORTED    125
 #define SCTP_SENDMSG_CONNECT   126
+#define SCTP_EVENT     127
 
 /* PR-SCTP policies */
 #define SCTP_PR_SCTP_NONE      0x0000
@@ -632,7 +633,9 @@ union sctp_notification {
  */
 
 enum sctp_sn_type {
-       SCTP_SN_TYPE_BASE     = (1<<15),
+       SCTP_SN_TYPE_BASE       = (1<<15),
+       SCTP_DATA_IO_EVENT      = SCTP_SN_TYPE_BASE,
+#define SCTP_DATA_IO_EVENT             SCTP_DATA_IO_EVENT
        SCTP_ASSOC_CHANGE,
 #define SCTP_ASSOC_CHANGE              SCTP_ASSOC_CHANGE
        SCTP_PEER_ADDR_CHANGE,
@@ -657,6 +660,8 @@ enum sctp_sn_type {
 #define SCTP_ASSOC_RESET_EVENT         SCTP_ASSOC_RESET_EVENT
        SCTP_STREAM_CHANGE_EVENT,
 #define SCTP_STREAM_CHANGE_EVENT       SCTP_STREAM_CHANGE_EVENT
+       SCTP_SN_TYPE_MAX        = SCTP_STREAM_CHANGE_EVENT,
+#define SCTP_SN_TYPE_MAX               SCTP_SN_TYPE_MAX
 };
 
 /* Notification error codes used to fill up the error fields in some
@@ -1150,6 +1155,12 @@ struct sctp_add_streams {
        uint16_t sas_outstrms;
 };
 
+struct sctp_event {
+       sctp_assoc_t se_assoc_id;
+       uint16_t se_type;
+       uint8_t se_on;
+};
+
 /* SCTP Stream schedulers */
 enum sctp_sched_type {
        SCTP_SS_FCFS,
index f80135e..86dc24a 100644 (file)
@@ -243,6 +243,7 @@ enum
        LINUX_MIB_TCPREQQFULLDROP,              /* TCPReqQFullDrop */
        LINUX_MIB_TCPRETRANSFAIL,               /* TCPRetransFail */
        LINUX_MIB_TCPRCVCOALESCE,               /* TCPRcvCoalesce */
+       LINUX_MIB_TCPBACKLOGCOALESCE,           /* TCPBacklogCoalesce */
        LINUX_MIB_TCPOFOQUEUE,                  /* TCPOFOQueue */
        LINUX_MIB_TCPOFODROP,                   /* TCPOFODrop */
        LINUX_MIB_TCPOFOMERGE,                  /* TCPOFOMerge */
index e02d319..8bb6cc5 100644 (file)
@@ -266,6 +266,7 @@ enum {
        TCP_NLA_BYTES_RETRANS,  /* Data bytes retransmitted */
        TCP_NLA_DSACK_DUPS,     /* DSACK blocks received */
        TCP_NLA_REORD_SEEN,     /* reordering events seen */
+       TCP_NLA_SRTT,           /* smoothed RTT in usecs */
 };
 
 /* for TCP_MD5SIG socket option */
index 09502de..30baccb 100644 (file)
@@ -33,6 +33,7 @@ struct udphdr {
 #define UDP_NO_CHECK6_TX 101   /* Disable sending checksum for UDP6X */
 #define UDP_NO_CHECK6_RX 102   /* Disable accpeting checksum for UDP6 */
 #define UDP_SEGMENT    103     /* Set GSO segmentation size */
+#define UDP_GRO                104     /* This socket can receive UDP GRO packets */
 
 /* UDP encapsulation types */
 #define UDP_ENCAP_ESPINUDP_NON_IKE     1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
index 449132c..1196e1c 100644 (file)
@@ -75,6 +75,9 @@
  */
 #define VIRTIO_F_IOMMU_PLATFORM                33
 
+/* This feature indicates support for the packed virtqueue layout. */
+#define VIRTIO_F_RING_PACKED           34
+
 /*
  * Does the device support Single Root I/O Virtualization?
  */
index 6d5d5fa..2414f8a 100644 (file)
 /* This means the buffer contains a list of buffer descriptors. */
 #define VRING_DESC_F_INDIRECT  4
 
+/*
+ * Mark a descriptor as available or used in packed ring.
+ * Notice: they are defined as shifts instead of shifted values.
+ */
+#define VRING_PACKED_DESC_F_AVAIL      7
+#define VRING_PACKED_DESC_F_USED       15
+
 /* The Host uses this in used->flags to advise the Guest: don't kick me when
  * you add a buffer.  It's unreliable, so it's simply an optimization.  Guest
  * will still kick if it's out of buffers. */
  * optimization.  */
 #define VRING_AVAIL_F_NO_INTERRUPT     1
 
+/* Enable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_ENABLE 0x0
+/* Disable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_DISABLE        0x1
+/*
+ * Enable events for a specific descriptor in packed ring.
+ * (as specified by Descriptor Ring Change Event Offset/Wrap Counter).
+ * Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated.
+ */
+#define VRING_PACKED_EVENT_FLAG_DESC   0x2
+
+/*
+ * Wrap counter bit shift in event suppression structure
+ * of packed ring.
+ */
+#define VRING_PACKED_EVENT_F_WRAP_CTR  15
+
 /* We support indirect buffer descriptors */
 #define VIRTIO_RING_F_INDIRECT_DESC    28
 
@@ -171,4 +195,32 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
        return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
 }
 
+struct vring_packed_desc_event {
+       /* Descriptor Ring Change Event Offset/Wrap Counter. */
+       __le16 off_wrap;
+       /* Descriptor Ring Change Event Flags. */
+       __le16 flags;
+};
+
+struct vring_packed_desc {
+       /* Buffer Address. */
+       __le64 addr;
+       /* Buffer Length. */
+       __le32 len;
+       /* Buffer ID. */
+       __le16 id;
+       /* The flags depending on descriptor type. */
+       __le16 flags;
+};
+
+struct vring_packed {
+       unsigned int num;
+
+       struct vring_packed_desc *desc;
+
+       struct vring_packed_desc_event *driver;
+
+       struct vring_packed_desc_event *device;
+};
+
 #endif /* _UAPI_LINUX_VIRTIO_RING_H */
index 4da543d..793acba 100644 (file)
@@ -260,6 +260,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE]     = "VOLATILE",
        [BTF_KIND_CONST]        = "CONST",
        [BTF_KIND_RESTRICT]     = "RESTRICT",
+       [BTF_KIND_FUNC]         = "FUNC",
+       [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
 };
 
 struct btf_kind_operations {
@@ -282,6 +284,9 @@ struct btf_kind_operations {
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS];
 static struct btf_type btf_void;
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id);
+
 static bool btf_type_is_modifier(const struct btf_type *t)
 {
        /* Some of them is not strictly a C modifier
@@ -307,15 +312,33 @@ static bool btf_type_is_modifier(const struct btf_type *t)
 
 static bool btf_type_is_void(const struct btf_type *t)
 {
-       /* void => no type and size info.
-        * Hence, FWD is also treated as void.
-        */
-       return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
+       return t == &btf_void;
+}
+
+static bool btf_type_is_fwd(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
+}
+
+static bool btf_type_is_func(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC;
+}
+
+static bool btf_type_is_func_proto(const struct btf_type *t)
+{
+       return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO;
+}
+
+static bool btf_type_nosize(const struct btf_type *t)
+{
+       return btf_type_is_void(t) || btf_type_is_fwd(t) ||
+              btf_type_is_func(t) || btf_type_is_func_proto(t);
 }
 
-static bool btf_type_is_void_or_null(const struct btf_type *t)
+static bool btf_type_nosize_or_null(const struct btf_type *t)
 {
-       return !t || btf_type_is_void(t);
+       return !t || btf_type_nosize(t);
 }
 
 /* union is only a special case of struct:
@@ -451,7 +474,7 @@ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
        return !*src;
 }
 
-static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
+const char *btf_name_by_offset(const struct btf *btf, u32 offset)
 {
        if (!offset)
                return "(anon)";
@@ -461,7 +484,7 @@ static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
                return "(invalid-name-offset)";
 }
 
-static const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
+const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
 {
        if (type_id > btf->nr_types)
                return NULL;
@@ -765,11 +788,15 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
                /* int, enum or void is a sink */
                return !btf_type_needs_resolve(next_type);
        case RESOLVE_PTR:
-               /* int, enum, void, struct or array is a sink for ptr */
+               /* int, enum, void, struct, array, func or func_proto is a sink
+                * for ptr
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_ptr(next_type);
        case RESOLVE_STRUCT_OR_ARRAY:
-               /* int, enum, void or ptr is a sink for struct and array */
+               /* int, enum, void, ptr, func or func_proto is a sink
+                * for struct and array
+                */
                return !btf_type_is_modifier(next_type) &&
                        !btf_type_is_array(next_type) &&
                        !btf_type_is_struct(next_type);
@@ -851,7 +878,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
        u32 size = 0;
 
        size_type = btf_type_by_id(btf, size_type_id);
-       if (btf_type_is_void_or_null(size_type))
+       if (btf_type_nosize_or_null(size_type))
                return NULL;
 
        if (btf_type_has_size(size_type)) {
@@ -867,7 +894,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
                size = btf->resolved_sizes[size_type_id];
                size_type_id = btf->resolved_ids[size_type_id];
                size_type = btf_type_by_id(btf, size_type_id);
-               if (btf_type_is_void(size_type))
+               if (btf_type_nosize_or_null(size_type))
                        return NULL;
        }
 
@@ -1204,10 +1231,6 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "typedef void new_void", "const void"...etc */
-       if (btf_type_is_void(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1218,13 +1241,18 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
         * save us a few type-following when we use it later (e.g. in
         * pretty print).
         */
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_is_void(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, &next_type_size)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               /* "typedef void new_void", "const void"...etc */
+               if (!btf_type_is_void(next_type) &&
+                   !btf_type_is_fwd(next_type)) {
+                       btf_verifier_log_type(env, v->t, "Invalid type_id");
+                       return -EINVAL;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, next_type_size);
 
        return 0;
@@ -1237,7 +1265,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
        const struct btf_type *t = v->t;
        u32 next_type_id = t->type;
        struct btf *btf = env->btf;
-       u32 next_type_size = 0;
 
        next_type = btf_type_by_id(btf, next_type_id);
        if (!next_type) {
@@ -1245,10 +1272,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                return -EINVAL;
        }
 
-       /* "void *" */
-       if (btf_type_is_void(next_type))
-               goto resolved;
-
        if (!env_type_is_resolve_sink(env, next_type) &&
            !env_type_is_resolved(env, next_type_id))
                return env_stack_push(env, next_type, next_type_id);
@@ -1275,13 +1298,18 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
                                              resolved_type_id);
        }
 
-       if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
-           !btf_type_is_void(btf_type_id_resolve(btf, &next_type_id))) {
-               btf_verifier_log_type(env, v->t, "Invalid type_id");
-               return -EINVAL;
+       if (!btf_type_id_size(btf, &next_type_id, NULL)) {
+               if (env_type_is_resolved(env, next_type_id))
+                       next_type = btf_type_id_resolve(btf, &next_type_id);
+
+               if (!btf_type_is_void(next_type) &&
+                   !btf_type_is_fwd(next_type) &&
+                   !btf_type_is_func_proto(next_type)) {
+                       btf_verifier_log_type(env, v->t, "Invalid type_id");
+                       return -EINVAL;
+               }
        }
 
-resolved:
        env_stack_pop_resolved(env, next_type_id, 0);
 
        return 0;
@@ -1450,7 +1478,7 @@ static int btf_array_resolve(struct btf_verifier_env *env,
        /* Check array->index_type */
        index_type_id = array->index_type;
        index_type = btf_type_by_id(btf, index_type_id);
-       if (btf_type_is_void_or_null(index_type)) {
+       if (btf_type_nosize_or_null(index_type)) {
                btf_verifier_log_type(env, v->t, "Invalid index");
                return -EINVAL;
        }
@@ -1469,7 +1497,7 @@ static int btf_array_resolve(struct btf_verifier_env *env,
        /* Check array->type */
        elem_type_id = array->type;
        elem_type = btf_type_by_id(btf, elem_type_id);
-       if (btf_type_is_void_or_null(elem_type)) {
+       if (btf_type_nosize_or_null(elem_type)) {
                btf_verifier_log_type(env, v->t,
                                      "Invalid elem");
                return -EINVAL;
@@ -1635,7 +1663,7 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
 
                if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
                        btf_verifier_log_member(env, t, member,
-                                               "Memmber bits_offset exceeds its struct size");
+                                               "Member bits_offset exceeds its struct size");
                        return -EINVAL;
                }
 
@@ -1682,7 +1710,7 @@ static int btf_struct_resolve(struct btf_verifier_env *env,
                const struct btf_type *member_type = btf_type_by_id(env->btf,
                                                                member_type_id);
 
-               if (btf_type_is_void_or_null(member_type)) {
+               if (btf_type_nosize_or_null(member_type)) {
                        btf_verifier_log_member(env, v->t, member,
                                                "Invalid member");
                        return -EINVAL;
@@ -1862,6 +1890,232 @@ static struct btf_kind_operations enum_ops = {
        .seq_show = btf_enum_seq_show,
 };
 
+static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
+                                    const struct btf_type *t,
+                                    u32 meta_left)
+{
+       u32 meta_needed = btf_type_vlen(t) * sizeof(struct btf_param);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->name_off) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return meta_needed;
+}
+
+static void btf_func_proto_log(struct btf_verifier_env *env,
+                              const struct btf_type *t)
+{
+       const struct btf_param *args = (const struct btf_param *)(t + 1);
+       u16 nr_args = btf_type_vlen(t), i;
+
+       btf_verifier_log(env, "return=%u args=(", t->type);
+       if (!nr_args) {
+               btf_verifier_log(env, "void");
+               goto done;
+       }
+
+       if (nr_args == 1 && !args[0].type) {
+               /* Only one vararg */
+               btf_verifier_log(env, "vararg");
+               goto done;
+       }
+
+       btf_verifier_log(env, "%u %s", args[0].type,
+                        btf_name_by_offset(env->btf,
+                                           args[0].name_off));
+       for (i = 1; i < nr_args - 1; i++)
+               btf_verifier_log(env, ", %u %s", args[i].type,
+                                btf_name_by_offset(env->btf,
+                                                   args[i].name_off));
+
+       if (nr_args > 1) {
+               const struct btf_param *last_arg = &args[nr_args - 1];
+
+               if (last_arg->type)
+                       btf_verifier_log(env, ", %u %s", last_arg->type,
+                                        btf_name_by_offset(env->btf,
+                                                           last_arg->name_off));
+               else
+                       btf_verifier_log(env, ", vararg");
+       }
+
+done:
+       btf_verifier_log(env, ")");
+}
+
+static struct btf_kind_operations func_proto_ops = {
+       .check_meta = btf_func_proto_check_meta,
+       .resolve = btf_df_resolve,
+       /*
+        * BTF_KIND_FUNC_PROTO cannot be directly referred by
+        * a struct's member.
+        *
+        * It should be a funciton pointer instead.
+        * (i.e. struct's member -> BTF_KIND_PTR -> BTF_KIND_FUNC_PROTO)
+        *
+        * Hence, there is no btf_func_check_member().
+        */
+       .check_member = btf_df_check_member,
+       .log_details = btf_func_proto_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static s32 btf_func_check_meta(struct btf_verifier_env *env,
+                              const struct btf_type *t,
+                              u32 meta_left)
+{
+       if (!t->name_off ||
+           !btf_name_valid_identifier(env->btf, t->name_off)) {
+               btf_verifier_log_type(env, t, "Invalid name");
+               return -EINVAL;
+       }
+
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return 0;
+}
+
+static struct btf_kind_operations func_ops = {
+       .check_meta = btf_func_check_meta,
+       .resolve = btf_df_resolve,
+       .check_member = btf_df_check_member,
+       .log_details = btf_ref_type_log,
+       .seq_show = btf_df_seq_show,
+};
+
+static int btf_func_proto_check(struct btf_verifier_env *env,
+                               const struct btf_type *t)
+{
+       const struct btf_type *ret_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+       int err;
+
+       btf = env->btf;
+       args = (const struct btf_param *)(t + 1);
+       nr_args = btf_type_vlen(t);
+
+       /* Check func return type which could be "void" (t->type == 0) */
+       if (t->type) {
+               u32 ret_type_id = t->type;
+
+               ret_type = btf_type_by_id(btf, ret_type_id);
+               if (!ret_type) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+
+               if (btf_type_needs_resolve(ret_type) &&
+                   !env_type_is_resolved(env, ret_type_id)) {
+                       err = btf_resolve(env, ret_type, ret_type_id);
+                       if (err)
+                               return err;
+               }
+
+               /* Ensure the return type is a type that has a size */
+               if (!btf_type_id_size(btf, &ret_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid return type");
+                       return -EINVAL;
+               }
+       }
+
+       if (!nr_args)
+               return 0;
+
+       /* Last func arg type_id could be 0 if it is a vararg */
+       if (!args[nr_args - 1].type) {
+               if (args[nr_args - 1].name_off) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u",
+                                             nr_args);
+                       return -EINVAL;
+               }
+               nr_args--;
+       }
+
+       err = 0;
+       for (i = 0; i < nr_args; i++) {
+               const struct btf_type *arg_type;
+               u32 arg_type_id;
+
+               arg_type_id = args[i].type;
+               arg_type = btf_type_by_id(btf, arg_type_id);
+               if (!arg_type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (args[i].name_off &&
+                   (!btf_name_offset_valid(btf, args[i].name_off) ||
+                    !btf_name_valid_identifier(btf, args[i].name_off))) {
+                       btf_verifier_log_type(env, t,
+                                             "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (btf_type_needs_resolve(arg_type) &&
+                   !env_type_is_resolved(env, arg_type_id)) {
+                       err = btf_resolve(env, arg_type, arg_type_id);
+                       if (err)
+                               break;
+               }
+
+               if (!btf_type_id_size(btf, &arg_type_id, NULL)) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       err = -EINVAL;
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int btf_func_check(struct btf_verifier_env *env,
+                         const struct btf_type *t)
+{
+       const struct btf_type *proto_type;
+       const struct btf_param *args;
+       const struct btf *btf;
+       u16 nr_args, i;
+
+       btf = env->btf;
+       proto_type = btf_type_by_id(btf, t->type);
+
+       if (!proto_type || !btf_type_is_func_proto(proto_type)) {
+               btf_verifier_log_type(env, t, "Invalid type_id");
+               return -EINVAL;
+       }
+
+       args = (const struct btf_param *)(proto_type + 1);
+       nr_args = btf_type_vlen(proto_type);
+       for (i = 0; i < nr_args; i++) {
+               if (!args[i].name_off && args[i].type) {
+                       btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_INT] = &int_ops,
        [BTF_KIND_PTR] = &ptr_ops,
@@ -1874,6 +2128,8 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
        [BTF_KIND_VOLATILE] = &modifier_ops,
        [BTF_KIND_CONST] = &modifier_ops,
        [BTF_KIND_RESTRICT] = &modifier_ops,
+       [BTF_KIND_FUNC] = &func_ops,
+       [BTF_KIND_FUNC_PROTO] = &func_proto_ops,
 };
 
 static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -1945,30 +2201,6 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
        return 0;
 }
 
-static int btf_resolve(struct btf_verifier_env *env,
-                      const struct btf_type *t, u32 type_id)
-{
-       const struct resolve_vertex *v;
-       int err = 0;
-
-       env->resolve_mode = RESOLVE_TBD;
-       env_stack_push(env, t, type_id);
-       while (!err && (v = env_stack_peak(env))) {
-               env->log_type_id = v->type_id;
-               err = btf_type_ops(v->t)->resolve(env, v);
-       }
-
-       env->log_type_id = type_id;
-       if (err == -E2BIG)
-               btf_verifier_log_type(env, t,
-                                     "Exceeded max resolving depth:%u",
-                                     MAX_RESOLVE_DEPTH);
-       else if (err == -EEXIST)
-               btf_verifier_log_type(env, t, "Loop detected");
-
-       return err;
-}
-
 static bool btf_resolve_valid(struct btf_verifier_env *env,
                              const struct btf_type *t,
                              u32 type_id)
@@ -2002,6 +2234,39 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
        return false;
 }
 
+static int btf_resolve(struct btf_verifier_env *env,
+                      const struct btf_type *t, u32 type_id)
+{
+       u32 save_log_type_id = env->log_type_id;
+       const struct resolve_vertex *v;
+       int err = 0;
+
+       env->resolve_mode = RESOLVE_TBD;
+       env_stack_push(env, t, type_id);
+       while (!err && (v = env_stack_peak(env))) {
+               env->log_type_id = v->type_id;
+               err = btf_type_ops(v->t)->resolve(env, v);
+       }
+
+       env->log_type_id = type_id;
+       if (err == -E2BIG) {
+               btf_verifier_log_type(env, t,
+                                     "Exceeded max resolving depth:%u",
+                                     MAX_RESOLVE_DEPTH);
+       } else if (err == -EEXIST) {
+               btf_verifier_log_type(env, t, "Loop detected");
+       }
+
+       /* Final sanity check */
+       if (!err && !btf_resolve_valid(env, t, type_id)) {
+               btf_verifier_log_type(env, t, "Invalid resolve state");
+               err = -EINVAL;
+       }
+
+       env->log_type_id = save_log_type_id;
+       return err;
+}
+
 static int btf_check_all_types(struct btf_verifier_env *env)
 {
        struct btf *btf = env->btf;
@@ -2024,10 +2289,16 @@ static int btf_check_all_types(struct btf_verifier_env *env)
                                return err;
                }
 
-               if (btf_type_needs_resolve(t) &&
-                   !btf_resolve_valid(env, t, type_id)) {
-                       btf_verifier_log_type(env, t, "Invalid resolve state");
-                       return -EINVAL;
+               if (btf_type_is_func_proto(t)) {
+                       err = btf_func_proto_check(env, t);
+                       if (err)
+                               return err;
+               }
+
+               if (btf_type_is_func(t)) {
+                       err = btf_func_check(env, t);
+                       if (err)
+                               return err;
                }
        }
 
index b1a3545..f93ed66 100644 (file)
  * Kris Katterjohn - Added many additional checks in bpf_check_classic()
  */
 
+#include <uapi/linux/btf.h>
 #include <linux/filter.h>
 #include <linux/skbuff.h>
 #include <linux/vmalloc.h>
 #include <linux/random.h>
 #include <linux/moduleloader.h>
 #include <linux/bpf.h>
+#include <linux/btf.h>
 #include <linux/frame.h>
 #include <linux/rbtree_latch.h>
 #include <linux/kallsyms.h>
@@ -390,6 +392,8 @@ bpf_get_prog_addr_region(const struct bpf_prog *prog,
 static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
 {
        const char *end = sym + KSYM_NAME_LEN;
+       const struct btf_type *type;
+       const char *func_name;
 
        BUILD_BUG_ON(sizeof("bpf_prog_") +
                     sizeof(prog->tag) * 2 +
@@ -404,6 +408,16 @@ static void bpf_get_prog_name(const struct bpf_prog *prog, char *sym)
 
        sym += snprintf(sym, KSYM_NAME_LEN, "bpf_prog_");
        sym  = bin2hex(sym, prog->tag, sizeof(prog->tag));
+
+       /* prog->aux->name will be ignored if full btf name is available */
+       if (prog->aux->btf) {
+               type = btf_type_by_id(prog->aux->btf,
+                                     prog->aux->func_info[prog->aux->func_idx].type_id);
+               func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
+               snprintf(sym, (size_t)(end - sym), "_%s", func_name);
+               return;
+       }
+
        if (prog->aux->name[0])
                snprintf(sym, (size_t)(end - sym), "_%s", prog->aux->name);
        else
index 2c17902..4b7c767 100644 (file)
@@ -23,7 +23,7 @@
 
 #define HTAB_CREATE_FLAG_MASK                                          \
        (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE |    \
-        BPF_F_RDONLY | BPF_F_WRONLY)
+        BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
 
 struct bucket {
        struct hlist_nulls_head head;
@@ -244,6 +244,7 @@ static int htab_map_alloc_check(union bpf_attr *attr)
         */
        bool percpu_lru = (attr->map_flags & BPF_F_NO_COMMON_LRU);
        bool prealloc = !(attr->map_flags & BPF_F_NO_PREALLOC);
+       bool zero_seed = (attr->map_flags & BPF_F_ZERO_SEED);
        int numa_node = bpf_map_attr_numa_node(attr);
 
        BUILD_BUG_ON(offsetof(struct htab_elem, htab) !=
@@ -257,6 +258,10 @@ static int htab_map_alloc_check(union bpf_attr *attr)
                 */
                return -EPERM;
 
+       if (zero_seed && !capable(CAP_SYS_ADMIN))
+               /* Guard against local DoS, and discourage production use. */
+               return -EPERM;
+
        if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
                /* reserved bits should not be used */
                return -EINVAL;
@@ -373,7 +378,11 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
        if (!htab->buckets)
                goto free_htab;
 
-       htab->hashrnd = get_random_int();
+       if (htab->map.map_flags & BPF_F_ZERO_SEED)
+               htab->hashrnd = 0;
+       else
+               htab->hashrnd = get_random_int();
+
        for (i = 0; i < htab->n_buckets; i++) {
                INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
                raw_spin_lock_init(&htab->buckets[i].lock);
index bed9d48..b65017d 100644 (file)
@@ -7,8 +7,7 @@
 #include <linux/rbtree.h>
 #include <linux/slab.h>
 
-DEFINE_PER_CPU(struct bpf_cgroup_storage*,
-              bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
+DEFINE_PER_CPU(struct bpf_cgroup_storage*, bpf_cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE]);
 
 #ifdef CONFIG_CGROUP_BPF
 
index 9058317..bfd4882 100644 (file)
@@ -168,20 +168,59 @@ static size_t longest_prefix_match(const struct lpm_trie *trie,
                                   const struct lpm_trie_node *node,
                                   const struct bpf_lpm_trie_key *key)
 {
-       size_t prefixlen = 0;
-       size_t i;
+       u32 limit = min(node->prefixlen, key->prefixlen);
+       u32 prefixlen = 0, i = 0;
 
-       for (i = 0; i < trie->data_size; i++) {
-               size_t b;
+       BUILD_BUG_ON(offsetof(struct lpm_trie_node, data) % sizeof(u32));
+       BUILD_BUG_ON(offsetof(struct bpf_lpm_trie_key, data) % sizeof(u32));
 
-               b = 8 - fls(node->data[i] ^ key->data[i]);
-               prefixlen += b;
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(CONFIG_64BIT)
 
-               if (prefixlen >= node->prefixlen || prefixlen >= key->prefixlen)
-                       return min(node->prefixlen, key->prefixlen);
+       /* data_size >= 16 has very small probability.
+        * We do not use a loop for optimal code generation.
+        */
+       if (trie->data_size >= 8) {
+               u64 diff = be64_to_cpu(*(__be64 *)node->data ^
+                                      *(__be64 *)key->data);
+
+               prefixlen = 64 - fls64(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i = 8;
+       }
+#endif
+
+       while (trie->data_size >= i + 4) {
+               u32 diff = be32_to_cpu(*(__be32 *)&node->data[i] ^
+                                      *(__be32 *)&key->data[i]);
+
+               prefixlen += 32 - fls(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i += 4;
+       }
 
-               if (b < 8)
-                       break;
+       if (trie->data_size >= i + 2) {
+               u16 diff = be16_to_cpu(*(__be16 *)&node->data[i] ^
+                                      *(__be16 *)&key->data[i]);
+
+               prefixlen += 16 - fls(diff);
+               if (prefixlen >= limit)
+                       return limit;
+               if (diff)
+                       return prefixlen;
+               i += 2;
+       }
+
+       if (trie->data_size >= i + 1) {
+               prefixlen += 8 - fls(node->data[i] ^ key->data[i]);
+
+               if (prefixlen >= limit)
+                       return limit;
        }
 
        return prefixlen;
index 8e93c47..54cf2b9 100644 (file)
@@ -33,6 +33,7 @@
 static DECLARE_RWSEM(bpf_devs_lock);
 
 struct bpf_offload_dev {
+       const struct bpf_prog_offload_ops *ops;
        struct list_head netdevs;
 };
 
@@ -106,6 +107,7 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
                err = -EINVAL;
                goto err_unlock;
        }
+       offload->offdev = ondev->offdev;
        prog->aux->offload = offload;
        list_add_tail(&offload->offloads, &ondev->progs);
        dev_put(offload->netdev);
@@ -121,40 +123,20 @@ err_maybe_put:
        return err;
 }
 
-static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,
-                            struct netdev_bpf *data)
+int bpf_prog_offload_verifier_prep(struct bpf_prog *prog)
 {
-       struct bpf_prog_offload *offload = prog->aux->offload;
-       struct net_device *netdev;
-
-       ASSERT_RTNL();
-
-       if (!offload)
-               return -ENODEV;
-       netdev = offload->netdev;
-
-       data->command = cmd;
-
-       return netdev->netdev_ops->ndo_bpf(netdev, data);
-}
-
-int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
-{
-       struct netdev_bpf data = {};
-       int err;
-
-       data.verifier.prog = env->prog;
+       struct bpf_prog_offload *offload;
+       int ret = -ENODEV;
 
-       rtnl_lock();
-       err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data);
-       if (err)
-               goto exit_unlock;
+       down_read(&bpf_devs_lock);
+       offload = prog->aux->offload;
+       if (offload) {
+               ret = offload->offdev->ops->prepare(prog);
+               offload->dev_state = !ret;
+       }
+       up_read(&bpf_devs_lock);
 
-       env->prog->aux->offload->dev_ops = data.verifier.ops;
-       env->prog->aux->offload->dev_state = true;
-exit_unlock:
-       rtnl_unlock();
-       return err;
+       return ret;
 }
 
 int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
@@ -166,7 +148,8 @@ int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
        down_read(&bpf_devs_lock);
        offload = env->prog->aux->offload;
        if (offload)
-               ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
+               ret = offload->offdev->ops->insn_hook(env, insn_idx,
+                                                     prev_insn_idx);
        up_read(&bpf_devs_lock);
 
        return ret;
@@ -180,8 +163,8 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
        down_read(&bpf_devs_lock);
        offload = env->prog->aux->offload;
        if (offload) {
-               if (offload->dev_ops->finalize)
-                       ret = offload->dev_ops->finalize(env);
+               if (offload->offdev->ops->finalize)
+                       ret = offload->offdev->ops->finalize(env);
                else
                        ret = 0;
        }
@@ -193,12 +176,9 @@ int bpf_prog_offload_finalize(struct bpf_verifier_env *env)
 static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
 {
        struct bpf_prog_offload *offload = prog->aux->offload;
-       struct netdev_bpf data = {};
-
-       data.offload.prog = prog;
 
        if (offload->dev_state)
-               WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data));
+               offload->offdev->ops->destroy(prog);
 
        /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
        bpf_prog_free_id(prog, true);
@@ -210,24 +190,22 @@ static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
 
 void bpf_prog_offload_destroy(struct bpf_prog *prog)
 {
-       rtnl_lock();
        down_write(&bpf_devs_lock);
        if (prog->aux->offload)
                __bpf_prog_offload_destroy(prog);
        up_write(&bpf_devs_lock);
-       rtnl_unlock();
 }
 
 static int bpf_prog_offload_translate(struct bpf_prog *prog)
 {
-       struct netdev_bpf data = {};
-       int ret;
-
-       data.offload.prog = prog;
+       struct bpf_prog_offload *offload;
+       int ret = -ENODEV;
 
-       rtnl_lock();
-       ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);
-       rtnl_unlock();
+       down_read(&bpf_devs_lock);
+       offload = prog->aux->offload;
+       if (offload)
+               ret = offload->offdev->ops->translate(prog);
+       up_read(&bpf_devs_lock);
 
        return ret;
 }
@@ -655,7 +633,8 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(bpf_offload_dev_netdev_unregister);
 
-struct bpf_offload_dev *bpf_offload_dev_create(void)
+struct bpf_offload_dev *
+bpf_offload_dev_create(const struct bpf_prog_offload_ops *ops)
 {
        struct bpf_offload_dev *offdev;
        int err;
@@ -673,6 +652,7 @@ struct bpf_offload_dev *bpf_offload_dev_create(void)
        if (!offdev)
                return ERR_PTR(-ENOMEM);
 
+       offdev->ops = ops;
        INIT_LIST_HEAD(&offdev->netdevs);
 
        return offdev;
index cf5040f..85cbeec 100644 (file)
@@ -1213,6 +1213,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
                /* bpf_prog_free_id() must be called first */
                bpf_prog_free_id(prog, do_idr_lock);
                bpf_prog_kallsyms_del_all(prog);
+               btf_put(prog->aux->btf);
+               kvfree(prog->aux->func_info);
 
                call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
        }
@@ -1437,9 +1439,9 @@ bpf_prog_load_check_attach_type(enum bpf_prog_type prog_type,
 }
 
 /* last field in 'union bpf_attr' used by this command */
-#define        BPF_PROG_LOAD_LAST_FIELD expected_attach_type
+#define        BPF_PROG_LOAD_LAST_FIELD func_info_cnt
 
-static int bpf_prog_load(union bpf_attr *attr)
+static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
 {
        enum bpf_prog_type type = attr->prog_type;
        struct bpf_prog *prog;
@@ -1525,7 +1527,7 @@ static int bpf_prog_load(union bpf_attr *attr)
                goto free_prog;
 
        /* run eBPF verifier */
-       err = bpf_check(&prog, attr);
+       err = bpf_check(&prog, attr, uattr);
        if (err < 0)
                goto free_used_maps;
 
@@ -2079,6 +2081,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
                info.xlated_prog_len = 0;
                info.nr_jited_ksyms = 0;
                info.nr_jited_func_lens = 0;
+               info.func_info_cnt = 0;
                goto done;
        }
 
@@ -2216,6 +2219,37 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
                }
        }
 
+       if (prog->aux->btf) {
+               u32 krec_size = sizeof(struct bpf_func_info);
+               u32 ucnt, urec_size;
+
+               info.btf_id = btf_id(prog->aux->btf);
+
+               ucnt = info.func_info_cnt;
+               info.func_info_cnt = prog->aux->func_info_cnt;
+               urec_size = info.func_info_rec_size;
+               info.func_info_rec_size = krec_size;
+               if (ucnt) {
+                       /* expect passed-in urec_size is what the kernel expects */
+                       if (urec_size != info.func_info_rec_size)
+                               return -EINVAL;
+
+                       if (bpf_dump_raw_ok()) {
+                               char __user *user_finfo;
+
+                               user_finfo = u64_to_user_ptr(info.func_info);
+                               ucnt = min_t(u32, info.func_info_cnt, ucnt);
+                               if (copy_to_user(user_finfo, prog->aux->func_info,
+                                                krec_size * ucnt))
+                                       return -EFAULT;
+                       } else {
+                               info.func_info_cnt = 0;
+                       }
+               }
+       } else {
+               info.func_info_cnt = 0;
+       }
+
 done:
        if (copy_to_user(uinfo, &info, info_len) ||
            put_user(info_len, &uattr->info.info_len))
@@ -2501,7 +2535,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
                err = map_get_next_key(&attr);
                break;
        case BPF_PROG_LOAD:
-               err = bpf_prog_load(&attr);
+               err = bpf_prog_load(&attr, uattr);
                break;
        case BPF_OBJ_PIN:
                err = bpf_obj_pin(&attr);
index fc760d0..ee30eff 100644 (file)
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * General Public License for more details.
  */
+#include <uapi/linux/btf.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/bpf.h>
+#include <linux/btf.h>
 #include <linux/bpf_verifier.h>
 #include <linux/filter.h>
 #include <net/netlink.h>
@@ -1456,6 +1458,17 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
                verbose(env, "R%d offset is outside of the packet\n", regno);
                return err;
        }
+
+       /* __check_packet_access has made sure "off + size - 1" is within u16.
+        * reg->umax_value can't be bigger than MAX_PACKET_OFF which is 0xffff,
+        * otherwise find_good_pkt_pointers would have refused to set range info
+        * that __check_packet_access would have rejected this pkt access.
+        * Therefore, "off + reg->umax_value + size - 1" won't overflow u32.
+        */
+       env->prog->aux->max_pkt_offset =
+               max_t(u32, env->prog->aux->max_pkt_offset,
+                     off + reg->umax_value + size - 1);
+
        return err;
 }
 
@@ -4696,6 +4709,130 @@ err_free:
        return ret;
 }
 
+/* The minimum supported BTF func info size */
+#define MIN_BPF_FUNCINFO_SIZE  8
+#define MAX_FUNCINFO_REC_SIZE  252
+
+static int check_btf_func(struct bpf_prog *prog, struct bpf_verifier_env *env,
+                         union bpf_attr *attr, union bpf_attr __user *uattr)
+{
+       u32 i, nfuncs, urec_size, min_size, prev_offset;
+       u32 krec_size = sizeof(struct bpf_func_info);
+       struct bpf_func_info *krecord = NULL;
+       const struct btf_type *type;
+       void __user *urecord;
+       struct btf *btf;
+       int ret = 0;
+
+       nfuncs = attr->func_info_cnt;
+       if (!nfuncs)
+               return 0;
+
+       if (nfuncs != env->subprog_cnt) {
+               verbose(env, "number of funcs in func_info doesn't match number of subprogs\n");
+               return -EINVAL;
+       }
+
+       urec_size = attr->func_info_rec_size;
+       if (urec_size < MIN_BPF_FUNCINFO_SIZE ||
+           urec_size > MAX_FUNCINFO_REC_SIZE ||
+           urec_size % sizeof(u32)) {
+               verbose(env, "invalid func info rec size %u\n", urec_size);
+               return -EINVAL;
+       }
+
+       btf = btf_get_by_fd(attr->prog_btf_fd);
+       if (IS_ERR(btf)) {
+               verbose(env, "unable to get btf from fd\n");
+               return PTR_ERR(btf);
+       }
+
+       urecord = u64_to_user_ptr(attr->func_info);
+       min_size = min_t(u32, krec_size, urec_size);
+
+       krecord = kvcalloc(nfuncs, krec_size, GFP_KERNEL | __GFP_NOWARN);
+       if (!krecord) {
+               ret = -ENOMEM;
+               goto free_btf;
+       }
+
+       for (i = 0; i < nfuncs; i++) {
+               ret = bpf_check_uarg_tail_zero(urecord, krec_size, urec_size);
+               if (ret) {
+                       if (ret == -E2BIG) {
+                               verbose(env, "nonzero tailing record in func info");
+                               /* set the size kernel expects so loader can zero
+                                * out the rest of the record.
+                                */
+                               if (put_user(min_size, &uattr->func_info_rec_size))
+                                       ret = -EFAULT;
+                       }
+                       goto free_btf;
+               }
+
+               if (copy_from_user(&krecord[i], urecord, min_size)) {
+                       ret = -EFAULT;
+                       goto free_btf;
+               }
+
+               /* check insn_offset */
+               if (i == 0) {
+                       if (krecord[i].insn_offset) {
+                               verbose(env,
+                                       "nonzero insn_offset %u for the first func info record",
+                                       krecord[i].insn_offset);
+                               ret = -EINVAL;
+                               goto free_btf;
+                       }
+               } else if (krecord[i].insn_offset <= prev_offset) {
+                       verbose(env,
+                               "same or smaller insn offset (%u) than previous func info record (%u)",
+                               krecord[i].insn_offset, prev_offset);
+                       ret = -EINVAL;
+                       goto free_btf;
+               }
+
+               if (env->subprog_info[i].start != krecord[i].insn_offset) {
+                       verbose(env, "func_info BTF section doesn't match subprog layout in BPF program\n");
+                       ret = -EINVAL;
+                       goto free_btf;
+               }
+
+               /* check type_id */
+               type = btf_type_by_id(btf, krecord[i].type_id);
+               if (!type || BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) {
+                       verbose(env, "invalid type id %d in func info",
+                               krecord[i].type_id);
+                       ret = -EINVAL;
+                       goto free_btf;
+               }
+
+               prev_offset = krecord[i].insn_offset;
+               urecord += urec_size;
+       }
+
+       prog->aux->btf = btf;
+       prog->aux->func_info = krecord;
+       prog->aux->func_info_cnt = nfuncs;
+       return 0;
+
+free_btf:
+       btf_put(btf);
+       kvfree(krecord);
+       return ret;
+}
+
+static void adjust_btf_func(struct bpf_verifier_env *env)
+{
+       int i;
+
+       if (!env->prog->aux->func_info)
+               return;
+
+       for (i = 0; i < env->subprog_cnt; i++)
+               env->prog->aux->func_info[i].insn_offset = env->subprog_info[i].start;
+}
+
 /* check %cur's range satisfies %old's */
 static bool range_within(struct bpf_reg_state *old,
                         struct bpf_reg_state *cur)
@@ -5782,10 +5919,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
        int i, cnt, size, ctx_field_size, delta = 0;
        const int insn_cnt = env->prog->len;
        struct bpf_insn insn_buf[16], *insn;
+       u32 target_size, size_default, off;
        struct bpf_prog *new_prog;
        enum bpf_access_type type;
        bool is_narrower_load;
-       u32 target_size;
 
        if (ops->gen_prologue || env->seen_direct_write) {
                if (!ops->gen_prologue) {
@@ -5878,9 +6015,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                 * we will apply proper mask to the result.
                 */
                is_narrower_load = size < ctx_field_size;
+               size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
+               off = insn->off;
                if (is_narrower_load) {
-                       u32 size_default = bpf_ctx_off_adjust_machine(ctx_field_size);
-                       u32 off = insn->off;
                        u8 size_code;
 
                        if (type == BPF_WRITE) {
@@ -5908,12 +6045,23 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                }
 
                if (is_narrower_load && size < target_size) {
-                       if (ctx_field_size <= 4)
+                       u8 shift = (off & (size_default - 1)) * 8;
+
+                       if (ctx_field_size <= 4) {
+                               if (shift)
+                                       insn_buf[cnt++] = BPF_ALU32_IMM(BPF_RSH,
+                                                                       insn->dst_reg,
+                                                                       shift);
                                insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
                                                                (1 << size * 8) - 1);
-                       else
+                       } else {
+                               if (shift)
+                                       insn_buf[cnt++] = BPF_ALU64_IMM(BPF_RSH,
+                                                                       insn->dst_reg,
+                                                                       shift);
                                insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
                                                                (1 << size * 8) - 1);
+                       }
                }
 
                new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
@@ -5986,6 +6134,11 @@ static int jit_subprogs(struct bpf_verifier_env *env)
                if (bpf_prog_calc_tag(func[i]))
                        goto out_free;
                func[i]->is_func = 1;
+               func[i]->aux->func_idx = i;
+               /* the btf and func_info will be freed only at prog->aux */
+               func[i]->aux->btf = prog->aux->btf;
+               func[i]->aux->func_info = prog->aux->func_info;
+
                /* Use bpf_prog_F_tag to indicate functions in stack traces.
                 * Long term would need debug info to populate names
                 */
@@ -6213,6 +6366,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
                         */
                        prog->cb_access = 1;
                        env->prog->aux->stack_depth = MAX_BPF_STACK;
+                       env->prog->aux->max_pkt_offset = MAX_PACKET_OFF;
 
                        /* mark bpf_tail_call as different opcode to avoid
                         * conditional branch in the interpeter for every normal
@@ -6377,7 +6531,8 @@ static void free_states(struct bpf_verifier_env *env)
        kfree(env->explored_states);
 }
 
-int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
+int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
+             union bpf_attr __user *uattr)
 {
        struct bpf_verifier_env *env;
        struct bpf_verifier_log *log;
@@ -6431,7 +6586,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
                goto skip_full_check;
 
        if (bpf_prog_is_dev_bound(env->prog->aux)) {
-               ret = bpf_prog_offload_verifier_prep(env);
+               ret = bpf_prog_offload_verifier_prep(env->prog);
                if (ret)
                        goto skip_full_check;
        }
@@ -6449,6 +6604,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
        if (ret < 0)
                goto skip_full_check;
 
+       ret = check_btf_func(env->prog, env, attr, uattr);
+       if (ret < 0)
+               goto skip_full_check;
+
        ret = do_check(env);
        if (env->cur_state) {
                free_verifier_state(env->cur_state, true);
@@ -6506,6 +6665,9 @@ skip_full_check:
                convert_pseudo_ld_imm64(env);
        }
 
+       if (ret == 0)
+               adjust_btf_func(env);
+
 err_release_maps:
        if (!env->prog->aux->used_maps)
                /* if we didn't copy map pointers into bpf_prog_info, release
index a9965f4..7dbbcfe 100644 (file)
@@ -624,3 +624,6 @@ config GENERIC_LIB_CMPDI2
 
 config GENERIC_LIB_UCMPDI2
        bool
+
+config OBJAGG
+       tristate "objagg" if COMPILE_TEST
index 1af29b8..b3c91b9 100644 (file)
@@ -1976,6 +1976,16 @@ config TEST_MEMCAT_P
 
          If unsure, say N.
 
+config TEST_OBJAGG
+       tristate "Perform selftest on object aggreration manager"
+       default n
+       depends on OBJAGG
+       help
+         Enable this option to test object aggregation manager on boot
+         (or module load).
+
+         If unsure, say N.
+
 endif # RUNTIME_TESTING_MENU
 
 config MEMTEST
index db06d12..f5262d3 100644 (file)
@@ -75,6 +75,7 @@ obj-$(CONFIG_TEST_PARMAN) += test_parman.o
 obj-$(CONFIG_TEST_KMOD) += test_kmod.o
 obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
 obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
+obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
@@ -274,3 +275,4 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
 obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
 obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
 obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
+obj-$(CONFIG_OBJAGG) += objagg.o
index 6cf4778..8ef27c1 100644 (file)
 #include <linux/module.h>
 #include <linux/cordic.h>
 
-#define CORDIC_ANGLE_GEN       39797
-#define CORDIC_PRECISION_SHIFT 16
-#define        CORDIC_NUM_ITER         (CORDIC_PRECISION_SHIFT + 2)
-
-#define        FIXED(X)        ((s32)((X) << CORDIC_PRECISION_SHIFT))
-#define        FLOAT(X)        (((X) >= 0) \
-               ? ((((X) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1) \
-               : -((((-(X)) >> (CORDIC_PRECISION_SHIFT - 1)) + 1) >> 1))
-
 static const s32 arctan_table[] = {
        2949120,
        1740967,
@@ -64,16 +55,16 @@ struct cordic_iq cordic_calc_iq(s32 theta)
        coord.q = 0;
        angle = 0;
 
-       theta = FIXED(theta);
+       theta = CORDIC_FIXED(theta);
        signtheta = (theta < 0) ? -1 : 1;
-       theta = ((theta + FIXED(180) * signtheta) % FIXED(360)) -
-               FIXED(180) * signtheta;
+       theta = ((theta + CORDIC_FIXED(180) * signtheta) % CORDIC_FIXED(360)) -
+               CORDIC_FIXED(180) * signtheta;
 
-       if (FLOAT(theta) > 90) {
-               theta -= FIXED(180);
+       if (CORDIC_FLOAT(theta) > 90) {
+               theta -= CORDIC_FIXED(180);
                signx = -1;
-       } else if (FLOAT(theta) < -90) {
-               theta += FIXED(180);
+       } else if (CORDIC_FLOAT(theta) < -90) {
+               theta += CORDIC_FIXED(180);
                signx = -1;
        }
 
diff --git a/lib/objagg.c b/lib/objagg.c
new file mode 100644 (file)
index 0000000..c9b457a
--- /dev/null
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rhashtable.h>
+#include <linux/list.h>
+#include <linux/sort.h>
+#include <linux/objagg.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/objagg.h>
+
+struct objagg {
+       const struct objagg_ops *ops;
+       void *priv;
+       struct rhashtable obj_ht;
+       struct rhashtable_params ht_params;
+       struct list_head obj_list;
+       unsigned int obj_count;
+};
+
+struct objagg_obj {
+       struct rhash_head ht_node; /* member of objagg->obj_ht */
+       struct list_head list; /* member of objagg->obj_list */
+       struct objagg_obj *parent; /* if the object is nested, this
+                                   * holds pointer to parent, otherwise NULL
+                                   */
+       union {
+               void *delta_priv; /* user delta private */
+               void *root_priv; /* user root private */
+       };
+       unsigned int refcount; /* counts number of users of this object
+                               * including nested objects
+                               */
+       struct objagg_obj_stats stats;
+       unsigned long obj[0];
+};
+
+static unsigned int objagg_obj_ref_inc(struct objagg_obj *objagg_obj)
+{
+       return ++objagg_obj->refcount;
+}
+
+static unsigned int objagg_obj_ref_dec(struct objagg_obj *objagg_obj)
+{
+       return --objagg_obj->refcount;
+}
+
+static void objagg_obj_stats_inc(struct objagg_obj *objagg_obj)
+{
+       objagg_obj->stats.user_count++;
+       objagg_obj->stats.delta_user_count++;
+       if (objagg_obj->parent)
+               objagg_obj->parent->stats.delta_user_count++;
+}
+
+static void objagg_obj_stats_dec(struct objagg_obj *objagg_obj)
+{
+       objagg_obj->stats.user_count--;
+       objagg_obj->stats.delta_user_count--;
+       if (objagg_obj->parent)
+               objagg_obj->parent->stats.delta_user_count--;
+}
+
+static bool objagg_obj_is_root(const struct objagg_obj *objagg_obj)
+{
+       /* Nesting is not supported, so we can use ->parent
+        * to figure out if the object is root.
+        */
+       return !objagg_obj->parent;
+}
+
+/**
+ * objagg_obj_root_priv - obtains root private for an object
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Either the object is root itself when the private is returned
+ * directly, or the parent is root and its private is returned
+ * instead.
+ *
+ * Returns a user private root pointer.
+ */
+const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj)
+{
+       if (objagg_obj_is_root(objagg_obj))
+               return objagg_obj->root_priv;
+       WARN_ON(!objagg_obj_is_root(objagg_obj->parent));
+       return objagg_obj->parent->root_priv;
+}
+EXPORT_SYMBOL(objagg_obj_root_priv);
+
+/**
+ * objagg_obj_delta_priv - obtains delta private for an object
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Returns user private delta pointer or NULL in case the passed
+ * object is root.
+ */
+const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj)
+{
+       if (objagg_obj_is_root(objagg_obj))
+               return NULL;
+       return objagg_obj->delta_priv;
+}
+EXPORT_SYMBOL(objagg_obj_delta_priv);
+
+/**
+ * objagg_obj_raw - obtains object user private pointer
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Returns user private pointer as was passed to objagg_obj_get() by "obj" arg.
+ */
+const void *objagg_obj_raw(const struct objagg_obj *objagg_obj)
+{
+       return objagg_obj->obj;
+}
+EXPORT_SYMBOL(objagg_obj_raw);
+
+static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj)
+{
+       return rhashtable_lookup_fast(&objagg->obj_ht, obj, objagg->ht_params);
+}
+
+static int objagg_obj_parent_assign(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj,
+                                   struct objagg_obj *parent)
+{
+       void *delta_priv;
+
+       delta_priv = objagg->ops->delta_create(objagg->priv, parent->obj,
+                                              objagg_obj->obj);
+       if (IS_ERR(delta_priv))
+               return PTR_ERR(delta_priv);
+
+       /* User returned a delta private, that means that
+        * our object can be aggregated into the parent.
+        */
+       objagg_obj->parent = parent;
+       objagg_obj->delta_priv = delta_priv;
+       objagg_obj_ref_inc(objagg_obj->parent);
+       trace_objagg_obj_parent_assign(objagg, objagg_obj,
+                                      parent,
+                                      parent->refcount);
+       return 0;
+}
+
+static int objagg_obj_parent_lookup_assign(struct objagg *objagg,
+                                          struct objagg_obj *objagg_obj)
+{
+       struct objagg_obj *objagg_obj_cur;
+       int err;
+
+       list_for_each_entry(objagg_obj_cur, &objagg->obj_list, list) {
+               /* Nesting is not supported. In case the object
+                * is not root, it cannot be assigned as parent.
+                */
+               if (!objagg_obj_is_root(objagg_obj_cur))
+                       continue;
+               err = objagg_obj_parent_assign(objagg, objagg_obj,
+                                              objagg_obj_cur);
+               if (!err)
+                       return 0;
+       }
+       return -ENOENT;
+}
+
+static void __objagg_obj_put(struct objagg *objagg,
+                            struct objagg_obj *objagg_obj);
+
+static void objagg_obj_parent_unassign(struct objagg *objagg,
+                                      struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_parent_unassign(objagg, objagg_obj,
+                                        objagg_obj->parent,
+                                        objagg_obj->parent->refcount);
+       objagg->ops->delta_destroy(objagg->priv, objagg_obj->delta_priv);
+       __objagg_obj_put(objagg, objagg_obj->parent);
+}
+
+static int objagg_obj_root_create(struct objagg *objagg,
+                                 struct objagg_obj *objagg_obj)
+{
+       objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
+                                                        objagg_obj->obj);
+       if (IS_ERR(objagg_obj->root_priv))
+               return PTR_ERR(objagg_obj->root_priv);
+
+       trace_objagg_obj_root_create(objagg, objagg_obj);
+       return 0;
+}
+
+static void objagg_obj_root_destroy(struct objagg *objagg,
+                                   struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_root_destroy(objagg, objagg_obj);
+       objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv);
+}
+
+static int objagg_obj_init(struct objagg *objagg,
+                          struct objagg_obj *objagg_obj)
+{
+       int err;
+
+       /* Try to find if the object can be aggregated under an existing one. */
+       err = objagg_obj_parent_lookup_assign(objagg, objagg_obj);
+       if (!err)
+               return 0;
+       /* If aggregation is not possible, make the object a root. */
+       return objagg_obj_root_create(objagg, objagg_obj);
+}
+
+static void objagg_obj_fini(struct objagg *objagg,
+                           struct objagg_obj *objagg_obj)
+{
+       if (!objagg_obj_is_root(objagg_obj))
+               objagg_obj_parent_unassign(objagg, objagg_obj);
+       else
+               objagg_obj_root_destroy(objagg, objagg_obj);
+}
+
+static struct objagg_obj *objagg_obj_create(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+       int err;
+
+       objagg_obj = kzalloc(sizeof(*objagg_obj) + objagg->ops->obj_size,
+                            GFP_KERNEL);
+       if (!objagg_obj)
+               return ERR_PTR(-ENOMEM);
+       objagg_obj_ref_inc(objagg_obj);
+       memcpy(objagg_obj->obj, obj, objagg->ops->obj_size);
+
+       err = objagg_obj_init(objagg, objagg_obj);
+       if (err)
+               goto err_obj_init;
+
+       err = rhashtable_insert_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+                                    objagg->ht_params);
+       if (err)
+               goto err_ht_insert;
+       list_add(&objagg_obj->list, &objagg->obj_list);
+       objagg->obj_count++;
+       trace_objagg_obj_create(objagg, objagg_obj);
+
+       return objagg_obj;
+
+err_ht_insert:
+       objagg_obj_fini(objagg, objagg_obj);
+err_obj_init:
+       kfree(objagg_obj);
+       return ERR_PTR(err);
+}
+
+static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+
+       /* First, try to find the object exactly as user passed it,
+        * perhaps it is already in use.
+        */
+       objagg_obj = objagg_obj_lookup(objagg, obj);
+       if (objagg_obj) {
+               objagg_obj_ref_inc(objagg_obj);
+               return objagg_obj;
+       }
+
+       return objagg_obj_create(objagg, obj);
+}
+
+/**
+ * objagg_obj_get - gets an object within objagg instance
+ * @objagg:    objagg instance
+ * @obj:       user-specific private object pointer
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Size of the "obj" memory is specified in "objagg->ops".
+ *
+ * There are 3 main options this function wraps:
+ * 1) The object according to "obj" already exist. In that case
+ *    the reference counter is incrementes and the object is returned.
+ * 2) The object does not exist, but it can be aggregated within
+ *    another object. In that case, user ops->delta_create() is called
+ *    to obtain delta data and a new object is created with returned
+ *    user-delta private pointer.
+ * 3) The object does not exist and cannot be aggregated into
+ *    any of the existing objects. In that case, user ops->root_create()
+ *    is called to create the root and a new object is created with
+ *    returned user-root private pointer.
+ *
+ * Returns a pointer to objagg object instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj)
+{
+       struct objagg_obj *objagg_obj;
+
+       objagg_obj = __objagg_obj_get(objagg, obj);
+       if (IS_ERR(objagg_obj))
+               return objagg_obj;
+       objagg_obj_stats_inc(objagg_obj);
+       trace_objagg_obj_get(objagg, objagg_obj, objagg_obj->refcount);
+       return objagg_obj;
+}
+EXPORT_SYMBOL(objagg_obj_get);
+
+static void objagg_obj_destroy(struct objagg *objagg,
+                              struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_destroy(objagg, objagg_obj);
+       --objagg->obj_count;
+       list_del(&objagg_obj->list);
+       rhashtable_remove_fast(&objagg->obj_ht, &objagg_obj->ht_node,
+                              objagg->ht_params);
+       objagg_obj_fini(objagg, objagg_obj);
+       kfree(objagg_obj);
+}
+
+static void __objagg_obj_put(struct objagg *objagg,
+                            struct objagg_obj *objagg_obj)
+{
+       if (!objagg_obj_ref_dec(objagg_obj))
+               objagg_obj_destroy(objagg, objagg_obj);
+}
+
+/**
+ * objagg_obj_put - puts an object within objagg instance
+ * @objagg:    objagg instance
+ * @objagg_obj:        objagg object instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * Symmetric to objagg_obj_get().
+ */
+void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj)
+{
+       trace_objagg_obj_put(objagg, objagg_obj, objagg_obj->refcount);
+       objagg_obj_stats_dec(objagg_obj);
+       __objagg_obj_put(objagg, objagg_obj);
+}
+EXPORT_SYMBOL(objagg_obj_put);
+
+/**
+ * objagg_create - creates a new objagg instance
+ * @ops:       user-specific callbacks
+ * @priv:      pointer to a private data passed to the ops
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * The purpose of the library is to provide an infrastructure to
+ * aggregate user-specified objects. Library does not care about the type
+ * of the object. User fills-up ops which take care of the specific
+ * user object manipulation.
+ *
+ * As a very stupid example, consider integer numbers. For example
+ * number 8 as a root object. That can aggregate number 9 with delta 1,
+ * number 10 with delta 2, etc. This example is implemented as
+ * a part of a testing module in test_objagg.c file.
+ *
+ * Each objagg instance contains multiple trees. Each tree node is
+ * represented by "an object". In the current implementation there can be
+ * only roots and leafs nodes. Leaf nodes are called deltas.
+ * But in general, this can be easily extended for intermediate nodes.
+ * In that extension, a delta would be associated with all non-root
+ * nodes.
+ *
+ * Returns a pointer to newly created objagg instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
+{
+       struct objagg *objagg;
+       int err;
+
+       if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy ||
+                   !ops->delta_create || !ops->delta_destroy))
+               return ERR_PTR(-EINVAL);
+       objagg = kzalloc(sizeof(*objagg), GFP_KERNEL);
+       if (!objagg)
+               return ERR_PTR(-ENOMEM);
+       objagg->ops = ops;
+       objagg->priv = priv;
+       INIT_LIST_HEAD(&objagg->obj_list);
+
+       objagg->ht_params.key_len = ops->obj_size;
+       objagg->ht_params.key_offset = offsetof(struct objagg_obj, obj);
+       objagg->ht_params.head_offset = offsetof(struct objagg_obj, ht_node);
+
+       err = rhashtable_init(&objagg->obj_ht, &objagg->ht_params);
+       if (err)
+               goto err_rhashtable_init;
+
+       trace_objagg_create(objagg);
+       return objagg;
+
+err_rhashtable_init:
+       kfree(objagg);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL(objagg_create);
+
+/**
+ * objagg_destroy - destroys a new objagg instance
+ * @objagg:    objagg instance
+ *
+ * Note: all locking must be provided by the caller.
+ */
+void objagg_destroy(struct objagg *objagg)
+{
+       trace_objagg_destroy(objagg);
+       WARN_ON(!list_empty(&objagg->obj_list));
+       rhashtable_destroy(&objagg->obj_ht);
+       kfree(objagg);
+}
+EXPORT_SYMBOL(objagg_destroy);
+
+static int objagg_stats_info_sort_cmp_func(const void *a, const void *b)
+{
+       const struct objagg_obj_stats_info *stats_info1 = a;
+       const struct objagg_obj_stats_info *stats_info2 = b;
+
+       if (stats_info1->is_root != stats_info2->is_root)
+               return stats_info2->is_root - stats_info1->is_root;
+       if (stats_info1->stats.delta_user_count !=
+           stats_info2->stats.delta_user_count)
+               return stats_info2->stats.delta_user_count -
+                      stats_info1->stats.delta_user_count;
+       return stats_info2->stats.user_count - stats_info1->stats.user_count;
+}
+
+/**
+ * objagg_stats_get - obtains stats of the objagg instance
+ * @objagg:    objagg instance
+ *
+ * Note: all locking must be provided by the caller.
+ *
+ * The returned structure contains statistics of all object
+ * currently in use, ordered by following rules:
+ * 1) Root objects are always on lower indexes than the rest.
+ * 2) Objects with higher delta user count are always on lower
+ *    indexes.
+ * 3) In case more objects have the same delta user count,
+ *    the objects are ordered by user count.
+ *
+ * Returns a pointer to stats instance in case of success,
+ * otherwise it returns pointer error using ERR_PTR macro.
+ */
+const struct objagg_stats *objagg_stats_get(struct objagg *objagg)
+{
+       struct objagg_stats *objagg_stats;
+       struct objagg_obj *objagg_obj;
+       size_t alloc_size;
+       int i;
+
+       alloc_size = sizeof(*objagg_stats) +
+                    sizeof(objagg_stats->stats_info[0]) * objagg->obj_count;
+       objagg_stats = kzalloc(alloc_size, GFP_KERNEL);
+       if (!objagg_stats)
+               return ERR_PTR(-ENOMEM);
+
+       i = 0;
+       list_for_each_entry(objagg_obj, &objagg->obj_list, list) {
+               memcpy(&objagg_stats->stats_info[i].stats, &objagg_obj->stats,
+                      sizeof(objagg_stats->stats_info[0].stats));
+               objagg_stats->stats_info[i].objagg_obj = objagg_obj;
+               objagg_stats->stats_info[i].is_root =
+                                       objagg_obj_is_root(objagg_obj);
+               i++;
+       }
+       objagg_stats->stats_info_count = i;
+
+       sort(objagg_stats->stats_info, objagg_stats->stats_info_count,
+            sizeof(struct objagg_obj_stats_info),
+            objagg_stats_info_sort_cmp_func, NULL);
+
+       return objagg_stats;
+}
+EXPORT_SYMBOL(objagg_stats_get);
+
+/**
+ * objagg_stats_puts - puts stats of the objagg instance
+ * @objagg_stats:      objagg instance stats
+ *
+ * Note: all locking must be provided by the caller.
+ */
+void objagg_stats_put(const struct objagg_stats *objagg_stats)
+{
+       kfree(objagg_stats);
+}
+EXPORT_SYMBOL(objagg_stats_put);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Object aggregation manager");
index 30526af..852ffa5 100644 (file)
@@ -1179,8 +1179,7 @@ struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
                                            unsigned int hash)
 {
        const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *));
-       static struct rhash_head __rcu *rhnull =
-               (struct rhash_head __rcu *)NULLS_MARKER(0);
+       static struct rhash_head __rcu *rhnull;
        unsigned int index = hash & ((1 << tbl->nest) - 1);
        unsigned int size = tbl->size >> tbl->nest;
        unsigned int subhash = hash;
@@ -1198,8 +1197,11 @@ struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl,
                subhash >>= shift;
        }
 
-       if (!ntbl)
+       if (!ntbl) {
+               if (!rhnull)
+                       INIT_RHT_NULLS_HEAD(rhnull);
                return &rhnull;
+       }
 
        return &ntbl[subhash].bucket;
 
index aa22bca..f3e5707 100644 (file)
@@ -39,6 +39,7 @@
 #define SKB_HASH       0x1234aaab
 #define SKB_QUEUE_MAP  123
 #define SKB_VLAN_TCI   0xffff
+#define SKB_VLAN_PRESENT       1
 #define SKB_DEV_IFINDEX        577
 #define SKB_DEV_TYPE   588
 
@@ -725,8 +726,8 @@ static struct bpf_test tests[] = {
                CLASSIC,
                { },
                {
-                       { 1, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT },
-                       { 10, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT }
+                       { 1, SKB_VLAN_TCI },
+                       { 10, SKB_VLAN_TCI }
                },
        },
        {
@@ -739,8 +740,8 @@ static struct bpf_test tests[] = {
                CLASSIC,
                { },
                {
-                       { 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
-                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+                       { 1, SKB_VLAN_PRESENT },
+                       { 10, SKB_VLAN_PRESENT }
                },
        },
        {
@@ -5289,8 +5290,8 @@ static struct bpf_test tests[] = {
 #endif
                { },
                {
-                       {  1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
-                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+                       {  1, SKB_VLAN_PRESENT },
+                       { 10, SKB_VLAN_PRESENT }
                },
                .fill_helper = bpf_fill_maxinsns6,
                .expected_errcode = -ENOTSUPP,
@@ -6493,6 +6494,7 @@ static struct sk_buff *populate_skb(char *buf, int size)
        skb->hash = SKB_HASH;
        skb->queue_mapping = SKB_QUEUE_MAP;
        skb->vlan_tci = SKB_VLAN_TCI;
+       skb->vlan_present = SKB_VLAN_PRESENT;
        skb->vlan_proto = htons(ETH_P_IP);
        dev_net_set(&dev, &init_net);
        skb->dev = &dev;
diff --git a/lib/test_objagg.c b/lib/test_objagg.c
new file mode 100644 (file)
index 0000000..ab57144
--- /dev/null
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/objagg.h>
+
+struct tokey {
+       unsigned int id;
+};
+
+#define NUM_KEYS 32
+
+static int key_id_index(unsigned int key_id)
+{
+       if (key_id >= NUM_KEYS) {
+               WARN_ON(1);
+               return 0;
+       }
+       return key_id;
+}
+
+#define BUF_LEN 128
+
+struct world {
+       unsigned int root_count;
+       unsigned int delta_count;
+       char next_root_buf[BUF_LEN];
+       struct objagg_obj *objagg_objs[NUM_KEYS];
+       unsigned int key_refs[NUM_KEYS];
+};
+
+struct root {
+       struct tokey key;
+       char buf[BUF_LEN];
+};
+
+struct delta {
+       unsigned int key_id_diff;
+};
+
+static struct objagg_obj *world_obj_get(struct world *world,
+                                       struct objagg *objagg,
+                                       unsigned int key_id)
+{
+       struct objagg_obj *objagg_obj;
+       struct tokey key;
+       int err;
+
+       key.id = key_id;
+       objagg_obj = objagg_obj_get(objagg, &key);
+       if (IS_ERR(objagg_obj)) {
+               pr_err("Key %u: Failed to get object.\n", key_id);
+               return objagg_obj;
+       }
+       if (!world->key_refs[key_id_index(key_id)]) {
+               world->objagg_objs[key_id_index(key_id)] = objagg_obj;
+       } else if (world->objagg_objs[key_id_index(key_id)] != objagg_obj) {
+               pr_err("Key %u: God another object for the same key.\n",
+                      key_id);
+               err = -EINVAL;
+               goto err_key_id_check;
+       }
+       world->key_refs[key_id_index(key_id)]++;
+       return objagg_obj;
+
+err_key_id_check:
+       objagg_obj_put(objagg, objagg_obj);
+       return ERR_PTR(err);
+}
+
+static void world_obj_put(struct world *world, struct objagg *objagg,
+                         unsigned int key_id)
+{
+       struct objagg_obj *objagg_obj;
+
+       if (!world->key_refs[key_id_index(key_id)])
+               return;
+       objagg_obj = world->objagg_objs[key_id_index(key_id)];
+       objagg_obj_put(objagg, objagg_obj);
+       world->key_refs[key_id_index(key_id)]--;
+}
+
+#define MAX_KEY_ID_DIFF 5
+
+static void *delta_create(void *priv, void *parent_obj, void *obj)
+{
+       struct tokey *parent_key = parent_obj;
+       struct world *world = priv;
+       struct tokey *key = obj;
+       int diff = key->id - parent_key->id;
+       struct delta *delta;
+
+       if (diff < 0 || diff > MAX_KEY_ID_DIFF)
+               return ERR_PTR(-EINVAL);
+
+       delta = kzalloc(sizeof(*delta), GFP_KERNEL);
+       if (!delta)
+               return ERR_PTR(-ENOMEM);
+       delta->key_id_diff = diff;
+       world->delta_count++;
+       return delta;
+}
+
+static void delta_destroy(void *priv, void *delta_priv)
+{
+       struct delta *delta = delta_priv;
+       struct world *world = priv;
+
+       world->delta_count--;
+       kfree(delta);
+}
+
+static void *root_create(void *priv, void *obj)
+{
+       struct world *world = priv;
+       struct tokey *key = obj;
+       struct root *root;
+
+       root = kzalloc(sizeof(*root), GFP_KERNEL);
+       if (!root)
+               return ERR_PTR(-ENOMEM);
+       memcpy(&root->key, key, sizeof(root->key));
+       memcpy(root->buf, world->next_root_buf, sizeof(root->buf));
+       world->root_count++;
+       return root;
+}
+
+static void root_destroy(void *priv, void *root_priv)
+{
+       struct root *root = root_priv;
+       struct world *world = priv;
+
+       world->root_count--;
+       kfree(root);
+}
+
+static int test_nodelta_obj_get(struct world *world, struct objagg *objagg,
+                               unsigned int key_id, bool should_create_root)
+{
+       unsigned int orig_root_count = world->root_count;
+       struct objagg_obj *objagg_obj;
+       const struct root *root;
+       int err;
+
+       if (should_create_root)
+               prandom_bytes(world->next_root_buf,
+                             sizeof(world->next_root_buf));
+
+       objagg_obj = world_obj_get(world, objagg, key_id);
+       if (IS_ERR(objagg_obj)) {
+               pr_err("Key %u: Failed to get object.\n", key_id);
+               return PTR_ERR(objagg_obj);
+       }
+       if (should_create_root) {
+               if (world->root_count != orig_root_count + 1) {
+                       pr_err("Key %u: Root was not created\n", key_id);
+                       err = -EINVAL;
+                       goto err_check_root_count;
+               }
+       } else {
+               if (world->root_count != orig_root_count) {
+                       pr_err("Key %u: Root was incorrectly created\n",
+                              key_id);
+                       err = -EINVAL;
+                       goto err_check_root_count;
+               }
+       }
+       root = objagg_obj_root_priv(objagg_obj);
+       if (root->key.id != key_id) {
+               pr_err("Key %u: Root has unexpected key id\n", key_id);
+               err = -EINVAL;
+               goto err_check_key_id;
+       }
+       if (should_create_root &&
+           memcmp(world->next_root_buf, root->buf, sizeof(root->buf))) {
+               pr_err("Key %u: Buffer does not match the expected content\n",
+                      key_id);
+               err = -EINVAL;
+               goto err_check_buf;
+       }
+       return 0;
+
+err_check_buf:
+err_check_key_id:
+err_check_root_count:
+       objagg_obj_put(objagg, objagg_obj);
+       return err;
+}
+
+static int test_nodelta_obj_put(struct world *world, struct objagg *objagg,
+                               unsigned int key_id, bool should_destroy_root)
+{
+       unsigned int orig_root_count = world->root_count;
+
+       world_obj_put(world, objagg, key_id);
+
+       if (should_destroy_root) {
+               if (world->root_count != orig_root_count - 1) {
+                       pr_err("Key %u: Root was not destroyed\n", key_id);
+                       return -EINVAL;
+               }
+       } else {
+               if (world->root_count != orig_root_count) {
+                       pr_err("Key %u: Root was incorrectly destroyed\n",
+                              key_id);
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int check_stats_zero(struct objagg *objagg)
+{
+       const struct objagg_stats *stats;
+       int err = 0;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+
+       if (stats->stats_info_count != 0) {
+               pr_err("Stats: Object count is not zero while it should be\n");
+               err = -EINVAL;
+       }
+
+       objagg_stats_put(stats);
+       return err;
+}
+
+static int check_stats_nodelta(struct objagg *objagg)
+{
+       const struct objagg_stats *stats;
+       int i;
+       int err;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+
+       if (stats->stats_info_count != NUM_KEYS) {
+               pr_err("Stats: Unexpected object count (%u expected, %u returned)\n",
+                      NUM_KEYS, stats->stats_info_count);
+               err = -EINVAL;
+               goto stats_put;
+       }
+
+       for (i = 0; i < stats->stats_info_count; i++) {
+               if (stats->stats_info[i].stats.user_count != 2) {
+                       pr_err("Stats: incorrect user count\n");
+                       err = -EINVAL;
+                       goto stats_put;
+               }
+               if (stats->stats_info[i].stats.delta_user_count != 2) {
+                       pr_err("Stats: incorrect delta user count\n");
+                       err = -EINVAL;
+                       goto stats_put;
+               }
+       }
+       err = 0;
+
+stats_put:
+       objagg_stats_put(stats);
+       return err;
+}
+
+static void *delta_create_dummy(void *priv, void *parent_obj, void *obj)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static void delta_destroy_dummy(void *priv, void *delta_priv)
+{
+}
+
+static const struct objagg_ops nodelta_ops = {
+       .obj_size = sizeof(struct tokey),
+       .delta_create = delta_create_dummy,
+       .delta_destroy = delta_destroy_dummy,
+       .root_create = root_create,
+       .root_destroy = root_destroy,
+};
+
+static int test_nodelta(void)
+{
+       struct world world = {};
+       struct objagg *objagg;
+       int i;
+       int err;
+
+       objagg = objagg_create(&nodelta_ops, &world);
+       if (IS_ERR(objagg))
+               return PTR_ERR(objagg);
+
+       err = check_stats_zero(objagg);
+       if (err)
+               goto err_stats_first_zero;
+
+       /* First round of gets, the root objects should be created */
+       for (i = 0; i < NUM_KEYS; i++) {
+               err = test_nodelta_obj_get(&world, objagg, i, true);
+               if (err)
+                       goto err_obj_first_get;
+       }
+
+       /* Do the second round of gets, all roots are already created,
+        * make sure that no new root is created
+        */
+       for (i = 0; i < NUM_KEYS; i++) {
+               err = test_nodelta_obj_get(&world, objagg, i, false);
+               if (err)
+                       goto err_obj_second_get;
+       }
+
+       err = check_stats_nodelta(objagg);
+       if (err)
+               goto err_stats_nodelta;
+
+       for (i = NUM_KEYS - 1; i >= 0; i--) {
+               err = test_nodelta_obj_put(&world, objagg, i, false);
+               if (err)
+                       goto err_obj_first_put;
+       }
+       for (i = NUM_KEYS - 1; i >= 0; i--) {
+               err = test_nodelta_obj_put(&world, objagg, i, true);
+               if (err)
+                       goto err_obj_second_put;
+       }
+
+       err = check_stats_zero(objagg);
+       if (err)
+               goto err_stats_second_zero;
+
+       objagg_destroy(objagg);
+       return 0;
+
+err_stats_nodelta:
+err_obj_first_put:
+err_obj_second_get:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world, objagg, i);
+
+       i = NUM_KEYS;
+err_obj_first_get:
+err_obj_second_put:
+       for (i--; i >= 0; i--)
+               world_obj_put(&world, objagg, i);
+err_stats_first_zero:
+err_stats_second_zero:
+       objagg_destroy(objagg);
+       return err;
+}
+
+static const struct objagg_ops delta_ops = {
+       .obj_size = sizeof(struct tokey),
+       .delta_create = delta_create,
+       .delta_destroy = delta_destroy,
+       .root_create = root_create,
+       .root_destroy = root_destroy,
+};
+
+enum action {
+       ACTION_GET,
+       ACTION_PUT,
+};
+
+enum expect_delta {
+       EXPECT_DELTA_SAME,
+       EXPECT_DELTA_INC,
+       EXPECT_DELTA_DEC,
+};
+
+enum expect_root {
+       EXPECT_ROOT_SAME,
+       EXPECT_ROOT_INC,
+       EXPECT_ROOT_DEC,
+};
+
+struct expect_stats_info {
+       struct objagg_obj_stats stats;
+       bool is_root;
+       unsigned int key_id;
+};
+
+struct expect_stats {
+       unsigned int info_count;
+       struct expect_stats_info info[NUM_KEYS];
+};
+
+struct action_item {
+       unsigned int key_id;
+       enum action action;
+       enum expect_delta expect_delta;
+       enum expect_root expect_root;
+       struct expect_stats expect_stats;
+};
+
+#define EXPECT_STATS(count, ...)               \
+{                                              \
+       .info_count = count,                    \
+       .info = { __VA_ARGS__ }                 \
+}
+
+#define ROOT(key_id, user_count, delta_user_count)     \
+       {{user_count, delta_user_count}, true, key_id}
+
+#define DELTA(key_id, user_count)                      \
+       {{user_count, user_count}, false, key_id}
+
+static const struct action_item action_items[] = {
+       {
+               1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(1, ROOT(1, 1, 1)),
+       },      /* r: 1                 d: */
+       {
+               7, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(2, ROOT(1, 1, 1), ROOT(7, 1, 1)),
+       },      /* r: 1, 7              d: */
+       {
+               3, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(3, ROOT(1, 1, 2), ROOT(7, 1, 1),
+                               DELTA(3, 1)),
+       },      /* r: 1, 7              d: 3^1 */
+       {
+               5, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 1, 3), ROOT(7, 1, 1),
+                               DELTA(3, 1), DELTA(5, 1)),
+       },      /* r: 1, 7              d: 3^1, 5^1 */
+       {
+               3, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 1, 4), ROOT(7, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 7              d: 3^1, 3^1, 5^1 */
+       {
+               1, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(1, 2, 5), ROOT(7, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7           d: 3^1, 3^1, 5^1 */
+       {
+               30, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(5, ROOT(1, 2, 5), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 2), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(5, 1), DELTA(8, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1, 8^7 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 5), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(3, 2), DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 3^1, 5^1, 8^7, 8^7 */
+       {
+               3, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(6, ROOT(1, 2, 4), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(3, 1), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 3^1, 5^1, 8^7, 8^7 */
+       {
+               3, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(1, 2, 3), ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 1, 7, 30       d: 5^1, 8^7, 8^7 */
+       {
+               1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(1, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 1, 7, 30          d: 5^1, 8^7, 8^7 */
+       {
+               1, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(1, 0, 1),
+                               DELTA(8, 2), DELTA(5, 1)),
+       },      /* r: 7, 30             d: 5^1, 8^7, 8^7 */
+       {
+               5, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
+               EXPECT_STATS(3, ROOT(7, 1, 3), ROOT(30, 1, 1),
+                               DELTA(8, 2)),
+       },      /* r: 7, 30             d: 8^7, 8^7 */
+       {
+               5, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_INC,
+               EXPECT_STATS(4, ROOT(7, 1, 3), ROOT(30, 1, 1), ROOT(5, 1, 1),
+                               DELTA(8, 2)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7 */
+       {
+               6, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 4), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 3), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 3), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 2), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(7, 1, 2), ROOT(5, 1, 2), ROOT(30, 1, 1),
+                               DELTA(8, 1), DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 8^7, 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(4, ROOT(5, 1, 2), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(6, 1)),
+       },      /* r: 7, 30, 5          d: 6^5 */
+       {
+               8, ACTION_GET, EXPECT_DELTA_INC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(5, ROOT(5, 1, 3), ROOT(7, 1, 1), ROOT(30, 1, 1),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 7, 30, 5          d: 6^5, 8^5 */
+       {
+               7, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
+               EXPECT_STATS(4, ROOT(5, 1, 3), ROOT(30, 1, 1),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 30, 5             d: 6^5, 8^5 */
+       {
+               30, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_DEC,
+               EXPECT_STATS(3, ROOT(5, 1, 3),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r: 5                 d: 6^5, 8^5 */
+       {
+               5, ACTION_PUT, EXPECT_DELTA_SAME, EXPECT_ROOT_SAME,
+               EXPECT_STATS(3, ROOT(5, 0, 2),
+                               DELTA(6, 1), DELTA(8, 1)),
+       },      /* r:                   d: 6^5, 8^5 */
+       {
+               6, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_SAME,
+               EXPECT_STATS(2, ROOT(5, 0, 1),
+                               DELTA(8, 1)),
+       },      /* r:                   d: 6^5 */
+       {
+               8, ACTION_PUT, EXPECT_DELTA_DEC, EXPECT_ROOT_DEC,
+               EXPECT_STATS(0, ),
+       },      /* r:                   d: */
+};
+
+static int check_expect(struct world *world,
+                       const struct action_item *action_item,
+                       unsigned int orig_delta_count,
+                       unsigned int orig_root_count)
+{
+       unsigned int key_id = action_item->key_id;
+
+       switch (action_item->expect_delta) {
+       case EXPECT_DELTA_SAME:
+               if (orig_delta_count != world->delta_count) {
+                       pr_err("Key %u: Delta count changed while expected to remain the same.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_DELTA_INC:
+               if (WARN_ON(action_item->action == ACTION_PUT))
+                       return -EINVAL;
+               if (orig_delta_count + 1 != world->delta_count) {
+                       pr_err("Key %u: Delta count was not incremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_DELTA_DEC:
+               if (WARN_ON(action_item->action == ACTION_GET))
+                       return -EINVAL;
+               if (orig_delta_count - 1 != world->delta_count) {
+                       pr_err("Key %u: Delta count was not decremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       }
+
+       switch (action_item->expect_root) {
+       case EXPECT_ROOT_SAME:
+               if (orig_root_count != world->root_count) {
+                       pr_err("Key %u: Root count changed while expected to remain the same.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_ROOT_INC:
+               if (WARN_ON(action_item->action == ACTION_PUT))
+                       return -EINVAL;
+               if (orig_root_count + 1 != world->root_count) {
+                       pr_err("Key %u: Root count was not incremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+               break;
+       case EXPECT_ROOT_DEC:
+               if (WARN_ON(action_item->action == ACTION_GET))
+                       return -EINVAL;
+               if (orig_root_count - 1 != world->root_count) {
+                       pr_err("Key %u: Root count was not decremented.\n",
+                              key_id);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static unsigned int obj_to_key_id(struct objagg_obj *objagg_obj)
+{
+       const struct tokey *root_key;
+       const struct delta *delta;
+       unsigned int key_id;
+
+       root_key = objagg_obj_root_priv(objagg_obj);
+       key_id = root_key->id;
+       delta = objagg_obj_delta_priv(objagg_obj);
+       if (delta)
+               key_id += delta->key_id_diff;
+       return key_id;
+}
+
+static int
+check_expect_stats_nums(const struct objagg_obj_stats_info *stats_info,
+                       const struct expect_stats_info *expect_stats_info,
+                       const char **errmsg)
+{
+       if (stats_info->is_root != expect_stats_info->is_root) {
+               if (errmsg)
+                       *errmsg = "Incorrect root/delta indication";
+               return -EINVAL;
+       }
+       if (stats_info->stats.user_count !=
+           expect_stats_info->stats.user_count) {
+               if (errmsg)
+                       *errmsg = "Incorrect user count";
+               return -EINVAL;
+       }
+       if (stats_info->stats.delta_user_count !=
+           expect_stats_info->stats.delta_user_count) {
+               if (errmsg)
+                       *errmsg = "Incorrect delta user count";
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+check_expect_stats_key_id(const struct objagg_obj_stats_info *stats_info,
+                         const struct expect_stats_info *expect_stats_info,
+                         const char **errmsg)
+{
+       if (obj_to_key_id(stats_info->objagg_obj) !=
+           expect_stats_info->key_id) {
+               if (errmsg)
+                       *errmsg = "incorrect key id";
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int check_expect_stats_neigh(const struct objagg_stats *stats,
+                                   const struct expect_stats *expect_stats,
+                                   int pos)
+{
+       int i;
+       int err;
+
+       for (i = pos - 1; i >= 0; i--) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[pos], NULL);
+               if (err)
+                       break;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[pos], NULL);
+               if (!err)
+                       return 0;
+       }
+       for (i = pos + 1; i < stats->stats_info_count; i++) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[pos], NULL);
+               if (err)
+                       break;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[pos], NULL);
+               if (!err)
+                       return 0;
+       }
+       return -EINVAL;
+}
+
+static int __check_expect_stats(const struct objagg_stats *stats,
+                               const struct expect_stats *expect_stats,
+                               const char **errmsg)
+{
+       int i;
+       int err;
+
+       if (stats->stats_info_count != expect_stats->info_count) {
+               *errmsg = "Unexpected object count";
+               return -EINVAL;
+       }
+
+       for (i = 0; i < stats->stats_info_count; i++) {
+               err = check_expect_stats_nums(&stats->stats_info[i],
+                                             &expect_stats->info[i], errmsg);
+               if (err)
+                       return err;
+               err = check_expect_stats_key_id(&stats->stats_info[i],
+                                               &expect_stats->info[i], errmsg);
+               if (err) {
+                       /* It is possible that one of the neighbor stats with
+                        * same numbers have the correct key id, so check it
+                        */
+                       err = check_expect_stats_neigh(stats, expect_stats, i);
+                       if (err)
+                               return err;
+               }
+       }
+       return 0;
+}
+
+static int check_expect_stats(struct objagg *objagg,
+                             const struct expect_stats *expect_stats,
+                             const char **errmsg)
+{
+       const struct objagg_stats *stats;
+       int err;
+
+       stats = objagg_stats_get(objagg);
+       if (IS_ERR(stats))
+               return PTR_ERR(stats);
+       err = __check_expect_stats(stats, expect_stats, errmsg);
+       objagg_stats_put(stats);
+       return err;
+}
+
+static int test_delta_action_item(struct world *world,
+                                 struct objagg *objagg,
+                                 const struct action_item *action_item,
+                                 bool inverse)
+{
+       unsigned int orig_delta_count = world->delta_count;
+       unsigned int orig_root_count = world->root_count;
+       unsigned int key_id = action_item->key_id;
+       enum action action = action_item->action;
+       struct objagg_obj *objagg_obj;
+       const char *errmsg;
+       int err;
+
+       if (inverse)
+               action = action == ACTION_GET ? ACTION_PUT : ACTION_GET;
+
+       switch (action) {
+       case ACTION_GET:
+               objagg_obj = world_obj_get(world, objagg, key_id);
+               if (IS_ERR(objagg_obj))
+                       return PTR_ERR(objagg_obj);
+               break;
+       case ACTION_PUT:
+               world_obj_put(world, objagg, key_id);
+               break;
+       }
+
+       if (inverse)
+               return 0;
+       err = check_expect(world, action_item,
+                          orig_delta_count, orig_root_count);
+       if (err)
+               goto errout;
+
+       errmsg = NULL;
+       err = check_expect_stats(objagg, &action_item->expect_stats, &errmsg);
+       if (err) {
+               pr_err("Key %u: Stats: %s\n", action_item->key_id, errmsg);
+               goto errout;
+       }
+
+       return 0;
+
+errout:
+       /* This can only happen when action is not inversed.
+        * So in case of an error, cleanup by doing inverse action.
+        */
+       test_delta_action_item(world, objagg, action_item, true);
+       return err;
+}
+
+static int test_delta(void)
+{
+       struct world world = {};
+       struct objagg *objagg;
+       int i;
+       int err;
+
+       objagg = objagg_create(&delta_ops, &world);
+       if (IS_ERR(objagg))
+               return PTR_ERR(objagg);
+
+       for (i = 0; i < ARRAY_SIZE(action_items); i++) {
+               err = test_delta_action_item(&world, objagg,
+                                            &action_items[i], false);
+               if (err)
+                       goto err_do_action_item;
+       }
+
+       objagg_destroy(objagg);
+       return 0;
+
+err_do_action_item:
+       for (i--; i >= 0; i--)
+               test_delta_action_item(&world, objagg, &action_items[i], true);
+
+       objagg_destroy(objagg);
+       return err;
+}
+
+static int __init test_objagg_init(void)
+{
+       int err;
+
+       err = test_nodelta();
+       if (err)
+               return err;
+       return test_delta();
+}
+
+static void __exit test_objagg_exit(void)
+{
+}
+
+module_init(test_objagg_init);
+module_exit(test_objagg_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
+MODULE_DESCRIPTION("Test module for objagg");
index 5e99504..dc44111 100644 (file)
@@ -330,6 +330,7 @@ static void vlan_transfer_features(struct net_device *dev,
 
        vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
        vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE);
+       vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev);
 
        netdev_update_features(vlandev);
 }
@@ -357,6 +358,7 @@ static int __vlan_device_event(struct net_device *dev, unsigned long event)
 static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                             void *ptr)
 {
+       struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct vlan_group *grp;
        struct vlan_info *vlan_info;
@@ -459,7 +461,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
                        vlan = vlan_dev_priv(vlandev);
                        if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
-                               dev_change_flags(vlandev, flgs | IFF_UP);
+                               dev_change_flags(vlandev, flgs | IFF_UP,
+                                                extack);
                        netif_stacked_transfer_operstate(dev, vlandev);
                }
                break;
@@ -647,93 +650,6 @@ out:
        return err;
 }
 
-static struct sk_buff *vlan_gro_receive(struct list_head *head,
-                                       struct sk_buff *skb)
-{
-       const struct packet_offload *ptype;
-       unsigned int hlen, off_vlan;
-       struct sk_buff *pp = NULL;
-       struct vlan_hdr *vhdr;
-       struct sk_buff *p;
-       __be16 type;
-       int flush = 1;
-
-       off_vlan = skb_gro_offset(skb);
-       hlen = off_vlan + sizeof(*vhdr);
-       vhdr = skb_gro_header_fast(skb, off_vlan);
-       if (skb_gro_header_hard(skb, hlen)) {
-               vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
-               if (unlikely(!vhdr))
-                       goto out;
-       }
-
-       type = vhdr->h_vlan_encapsulated_proto;
-
-       rcu_read_lock();
-       ptype = gro_find_receive_by_type(type);
-       if (!ptype)
-               goto out_unlock;
-
-       flush = 0;
-
-       list_for_each_entry(p, head, list) {
-               struct vlan_hdr *vhdr2;
-
-               if (!NAPI_GRO_CB(p)->same_flow)
-                       continue;
-
-               vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
-               if (compare_vlan_header(vhdr, vhdr2))
-                       NAPI_GRO_CB(p)->same_flow = 0;
-       }
-
-       skb_gro_pull(skb, sizeof(*vhdr));
-       skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
-       pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
-
-out_unlock:
-       rcu_read_unlock();
-out:
-       skb_gro_flush_final(skb, pp, flush);
-
-       return pp;
-}
-
-static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
-{
-       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
-       __be16 type = vhdr->h_vlan_encapsulated_proto;
-       struct packet_offload *ptype;
-       int err = -ENOENT;
-
-       rcu_read_lock();
-       ptype = gro_find_complete_by_type(type);
-       if (ptype)
-               err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
-
-       rcu_read_unlock();
-       return err;
-}
-
-static struct packet_offload vlan_packet_offloads[] __read_mostly = {
-       {
-               .type = cpu_to_be16(ETH_P_8021Q),
-               .priority = 10,
-               .callbacks = {
-                       .gro_receive = vlan_gro_receive,
-                       .gro_complete = vlan_gro_complete,
-               },
-       },
-       {
-               .type = cpu_to_be16(ETH_P_8021AD),
-               .priority = 10,
-               .callbacks = {
-                       .gro_receive = vlan_gro_receive,
-                       .gro_complete = vlan_gro_complete,
-               },
-       },
-};
-
 static int __net_init vlan_init_net(struct net *net)
 {
        struct vlan_net *vn = net_generic(net, vlan_net_id);
@@ -761,7 +677,6 @@ static struct pernet_operations vlan_net_ops = {
 static int __init vlan_proto_init(void)
 {
        int err;
-       unsigned int i;
 
        pr_info("%s v%s\n", vlan_fullname, vlan_version);
 
@@ -785,9 +700,6 @@ static int __init vlan_proto_init(void)
        if (err < 0)
                goto err5;
 
-       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
-               dev_add_offload(&vlan_packet_offloads[i]);
-
        vlan_ioctl_set(vlan_ioctl_handler);
        return 0;
 
@@ -805,13 +717,8 @@ err0:
 
 static void __exit vlan_cleanup_module(void)
 {
-       unsigned int i;
-
        vlan_ioctl_set(NULL);
 
-       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
-               dev_remove_offload(&vlan_packet_offloads[i]);
-
        vlan_netlink_fini();
 
        unregister_netdevice_notifier(&vlan_notifier_block);
index 44df1c3..c46daf0 100644 (file)
@@ -92,6 +92,18 @@ static inline struct net_device *vlan_find_dev(struct net_device *real_dev,
        return NULL;
 }
 
+static inline netdev_features_t vlan_tnl_features(struct net_device *real_dev)
+{
+       netdev_features_t ret;
+
+       ret = real_dev->hw_enc_features &
+             (NETIF_F_CSUM_MASK | NETIF_F_ALL_TSO | NETIF_F_GSO_ENCAP_ALL);
+
+       if ((ret & NETIF_F_GSO_ENCAP_ALL) && (ret & NETIF_F_CSUM_MASK))
+               return (ret & ~NETIF_F_CSUM_MASK) | NETIF_F_HW_CSUM;
+       return 0;
+}
+
 #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, \
index 4f60e86..a313165 100644 (file)
@@ -57,7 +57,7 @@ bool vlan_do_receive(struct sk_buff **skbp)
        }
 
        skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci);
-       skb->vlan_tci = 0;
+       __vlan_hwaccel_clear_tag(skb);
 
        rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats);
 
@@ -223,6 +223,33 @@ static int vlan_kill_rx_filter_info(struct net_device *dev, __be16 proto, u16 vi
                return -ENODEV;
 }
 
+int vlan_for_each(struct net_device *dev,
+                 int (*action)(struct net_device *dev, int vid, void *arg),
+                 void *arg)
+{
+       struct vlan_vid_info *vid_info;
+       struct vlan_info *vlan_info;
+       struct net_device *vdev;
+       int ret;
+
+       ASSERT_RTNL();
+
+       vlan_info = rtnl_dereference(dev->vlan_info);
+       if (!vlan_info)
+               return 0;
+
+       list_for_each_entry(vid_info, &vlan_info->vid_list, list) {
+               vdev = vlan_group_get_device(&vlan_info->grp, vid_info->proto,
+                                            vid_info->vid);
+               ret = action(vdev, vid_info->vid, arg);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(vlan_for_each);
+
 int vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto)
 {
        struct net_device *real_dev = vlan_info->real_dev;
@@ -426,3 +453,102 @@ bool vlan_uses_dev(const struct net_device *dev)
        return vlan_info->grp.nr_vlan_devs ? true : false;
 }
 EXPORT_SYMBOL(vlan_uses_dev);
+
+static struct sk_buff *vlan_gro_receive(struct list_head *head,
+                                       struct sk_buff *skb)
+{
+       const struct packet_offload *ptype;
+       unsigned int hlen, off_vlan;
+       struct sk_buff *pp = NULL;
+       struct vlan_hdr *vhdr;
+       struct sk_buff *p;
+       __be16 type;
+       int flush = 1;
+
+       off_vlan = skb_gro_offset(skb);
+       hlen = off_vlan + sizeof(*vhdr);
+       vhdr = skb_gro_header_fast(skb, off_vlan);
+       if (skb_gro_header_hard(skb, hlen)) {
+               vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
+               if (unlikely(!vhdr))
+                       goto out;
+       }
+
+       type = vhdr->h_vlan_encapsulated_proto;
+
+       rcu_read_lock();
+       ptype = gro_find_receive_by_type(type);
+       if (!ptype)
+               goto out_unlock;
+
+       flush = 0;
+
+       list_for_each_entry(p, head, list) {
+               struct vlan_hdr *vhdr2;
+
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
+               if (compare_vlan_header(vhdr, vhdr2))
+                       NAPI_GRO_CB(p)->same_flow = 0;
+       }
+
+       skb_gro_pull(skb, sizeof(*vhdr));
+       skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
+       pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
+
+out_unlock:
+       rcu_read_unlock();
+out:
+       skb_gro_flush_final(skb, pp, flush);
+
+       return pp;
+}
+
+static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
+       __be16 type = vhdr->h_vlan_encapsulated_proto;
+       struct packet_offload *ptype;
+       int err = -ENOENT;
+
+       rcu_read_lock();
+       ptype = gro_find_complete_by_type(type);
+       if (ptype)
+               err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
+
+       rcu_read_unlock();
+       return err;
+}
+
+static struct packet_offload vlan_packet_offloads[] __read_mostly = {
+       {
+               .type = cpu_to_be16(ETH_P_8021Q),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+       {
+               .type = cpu_to_be16(ETH_P_8021AD),
+               .priority = 10,
+               .callbacks = {
+                       .gro_receive = vlan_gro_receive,
+                       .gro_complete = vlan_gro_complete,
+               },
+       },
+};
+
+static int __init vlan_offload_init(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
+               dev_add_offload(&vlan_packet_offloads[i]);
+
+       return 0;
+}
+
+fs_initcall(vlan_offload_init);
index ff720f1..b2d9c8f 100644 (file)
@@ -562,6 +562,7 @@ static int vlan_dev_init(struct net_device *dev)
 
        dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |
                           NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
+                          NETIF_F_GSO_ENCAP_ALL |
                           NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
                           NETIF_F_ALL_FCOE;
 
@@ -572,6 +573,7 @@ static int vlan_dev_init(struct net_device *dev)
                netdev_warn(real_dev, "VLAN features are set incorrectly.  Q-in-Q configurations may not work correctly.\n");
 
        dev->vlan_features = real_dev->vlan_features & ~NETIF_F_ALL_FCOE;
+       dev->hw_enc_features = vlan_tnl_features(real_dev);
 
        /* ipv6 shared card related stuff */
        dev->dev_id = real_dev->dev_id;
index f75816f..c386e69 100644 (file)
@@ -22,7 +22,6 @@
 config BATMAN_ADV
        tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
        depends on NET
-       select CRC16
        select LIBCRC32C
        help
           B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
@@ -48,6 +47,7 @@ config BATMAN_ADV_BATMAN_V
 config BATMAN_ADV_BLA
        bool "Bridge Loop Avoidance"
        depends on BATMAN_ADV && INET
+       select CRC16
        default y
        help
          This option enables BLA (Bridge Loop Avoidance), a mechanism
@@ -82,6 +82,7 @@ config BATMAN_ADV_NC
 config BATMAN_ADV_MCAST
        bool "Multicast optimisation"
        depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
+       default y
        help
          This option enables the multicast optimisation which aims to
          reduce the air overhead while improving the reliability of
@@ -100,12 +101,13 @@ config BATMAN_ADV_DEBUGFS
 
 config BATMAN_ADV_DEBUG
        bool "B.A.T.M.A.N. debugging"
-       depends on BATMAN_ADV_DEBUGFS
+       depends on BATMAN_ADV
        help
          This is an option for use by developers; most people should
          say N here. This enables compilation of support for
-         outputting debugging information to the kernel log. The
-         output is controlled via the module parameter debug.
+         outputting debugging information to the debugfs log or tracing
+         buffer. The output is controlled via the batadv netdev specific
+         log_level setting.
 
 config BATMAN_ADV_TRACING
        bool "B.A.T.M.A.N. tracing support"
index d222709..f97e566 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/list.h>
-#include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/pkt_sched.h>
@@ -2585,13 +2584,14 @@ static void batadv_iv_gw_print(struct batadv_priv *bat_priv,
  * batadv_iv_gw_dump_entry() - Dump a gateway into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @gw_node: Gateway to be dumped
  *
  * Return: Error code, or 0 on success
  */
-static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid,
+                                  struct netlink_callback *cb,
                                   struct batadv_priv *bat_priv,
                                   struct batadv_gw_node *gw_node)
 {
@@ -2611,13 +2611,16 @@ static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_GATEWAYS);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        ret = -EMSGSIZE;
 
        if (curr_gw == gw_node)
@@ -2668,13 +2671,15 @@ static void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
        int idx_skip = cb->args[0];
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
+       spin_lock_bh(&bat_priv->gw.list_lock);
+       cb->seq = bat_priv->gw.generation << 1 | 1;
+
+       hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
                if (idx++ < idx_skip)
                        continue;
 
-               if (batadv_iv_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
-                                           bat_priv, gw_node)) {
+               if (batadv_iv_gw_dump_entry(msg, portid, cb, bat_priv,
+                                           gw_node)) {
                        idx_skip = idx - 1;
                        goto unlock;
                }
@@ -2682,7 +2687,7 @@ static void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
 
        idx_skip = idx;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&bat_priv->gw.list_lock);
 
        cb->args[0] = idx_skip;
 }
index 6baec4e..90e33f8 100644 (file)
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/kref.h>
+#include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include <linux/seq_file.h>
+#include <linux/spinlock.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
@@ -915,13 +917,14 @@ static void batadv_v_gw_print(struct batadv_priv *bat_priv,
  * batadv_v_gw_dump_entry() - Dump a gateway into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @gw_node: Gateway to be dumped
  *
  * Return: Error code, or 0 on success
  */
-static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid,
+                                 struct netlink_callback *cb,
                                  struct batadv_priv *bat_priv,
                                  struct batadv_gw_node *gw_node)
 {
@@ -941,13 +944,16 @@ static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_GATEWAYS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_GATEWAYS);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        ret = -EMSGSIZE;
 
        if (curr_gw == gw_node) {
@@ -1018,13 +1024,15 @@ static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
        int idx_skip = cb->args[0];
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
+       spin_lock_bh(&bat_priv->gw.list_lock);
+       cb->seq = bat_priv->gw.generation << 1 | 1;
+
+       hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
                if (idx++ < idx_skip)
                        continue;
 
-               if (batadv_v_gw_dump_entry(msg, portid, cb->nlh->nlmsg_seq,
-                                          bat_priv, gw_node)) {
+               if (batadv_v_gw_dump_entry(msg, portid, cb, bat_priv,
+                                          gw_node)) {
                        idx_skip = idx - 1;
                        goto unlock;
                }
@@ -1032,7 +1040,7 @@ static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
 
        idx_skip = idx;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&bat_priv->gw.list_lock);
 
        cb->args[0] = idx_skip;
 }
index 5f1aeed..5fdde29 100644 (file)
@@ -2094,14 +2094,15 @@ out:
  * to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
  * @claim: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_hard_iface *primary_if,
                            struct batadv_bla_claim *claim)
 {
@@ -2111,13 +2112,16 @@ batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
        void *hdr;
        int ret = -EINVAL;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_BLA_CLAIM);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_BLA_CLAIM);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        is_own = batadv_compare_eth(claim->backbone_gw->orig,
                                    primary_addr);
 
@@ -2153,28 +2157,33 @@ out:
  * to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
- * @head: bucket to dump
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: always 0.
  */
 static int
-batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid,
+                            struct netlink_callback *cb,
                             struct batadv_hard_iface *primary_if,
-                            struct hlist_head *head, int *idx_skip)
+                            struct batadv_hashtable *hash, unsigned int bucket,
+                            int *idx_skip)
 {
        struct batadv_bla_claim *claim;
        int idx = 0;
        int ret = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(claim, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(claim, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_skip)
                        continue;
 
-               ret = batadv_bla_claim_dump_entry(msg, portid, seq,
+               ret = batadv_bla_claim_dump_entry(msg, portid, cb,
                                                  primary_if, claim);
                if (ret) {
                        *idx_skip = idx - 1;
@@ -2184,7 +2193,7 @@ batadv_bla_claim_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 
        *idx_skip = 0;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
        return ret;
 }
 
@@ -2204,7 +2213,6 @@ int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -2230,11 +2238,8 @@ int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_bla_claim_dump_bucket(msg, portid,
-                                                cb->nlh->nlmsg_seq,
-                                                primary_if, head, &idx))
+               if (batadv_bla_claim_dump_bucket(msg, portid, cb, primary_if,
+                                                hash, bucket, &idx))
                        break;
                bucket++;
        }
@@ -2325,14 +2330,15 @@ out:
  *  netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
  * @backbone_gw: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
+                              struct netlink_callback *cb,
                               struct batadv_hard_iface *primary_if,
                               struct batadv_bla_backbone_gw *backbone_gw)
 {
@@ -2343,13 +2349,16 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
        void *hdr;
        int ret = -EINVAL;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_BLA_BACKBONE);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_BLA_BACKBONE);
        if (!hdr) {
                ret = -ENOBUFS;
                goto out;
        }
 
+       genl_dump_check_consistent(cb, hdr);
+
        is_own = batadv_compare_eth(backbone_gw->orig, primary_addr);
 
        spin_lock_bh(&backbone_gw->crc_lock);
@@ -2386,28 +2395,33 @@ out:
  *  a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @primary_if: primary interface
- * @head: bucket to dump
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: always 0.
  */
 static int
-batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid,
+                               struct netlink_callback *cb,
                                struct batadv_hard_iface *primary_if,
-                               struct hlist_head *head, int *idx_skip)
+                               struct batadv_hashtable *hash,
+                               unsigned int bucket, int *idx_skip)
 {
        struct batadv_bla_backbone_gw *backbone_gw;
        int idx = 0;
        int ret = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(backbone_gw, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_skip)
                        continue;
 
-               ret = batadv_bla_backbone_dump_entry(msg, portid, seq,
+               ret = batadv_bla_backbone_dump_entry(msg, portid, cb,
                                                     primary_if, backbone_gw);
                if (ret) {
                        *idx_skip = idx - 1;
@@ -2417,7 +2431,7 @@ batadv_bla_backbone_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 
        *idx_skip = 0;
 unlock:
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
        return ret;
 }
 
@@ -2437,7 +2451,6 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -2463,11 +2476,8 @@ int batadv_bla_backbone_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_bla_backbone_dump_bucket(msg, portid,
-                                                   cb->nlh->nlmsg_seq,
-                                                   primary_if, head, &idx))
+               if (batadv_bla_backbone_dump_bucket(msg, portid, cb, primary_if,
+                                                   hash, bucket, &idx))
                        break;
                bucket++;
        }
index 8b608a2..d4a7702 100644 (file)
@@ -19,6 +19,7 @@
 #include "debugfs.h"
 #include "main.h"
 
+#include <asm/current.h>
 #include <linux/dcache.h>
 #include <linux/debugfs.h>
 #include <linux/err.h>
@@ -27,6 +28,7 @@
 #include <linux/fs.h>
 #include <linux/netdevice.h>
 #include <linux/printk.h>
+#include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/stat.h>
 #include <linux/stddef.h>
index a60bacf..b9ffe18 100644 (file)
@@ -863,23 +863,27 @@ out:
  *  netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @dat_entry: entry to dump
  *
  * Return: 0 or error code.
  */
 static int
-batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_dat_entry *dat_entry)
 {
        int msecs;
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_DAT_CACHE);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_DAT_CACHE);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
 
        if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
@@ -901,27 +905,31 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  *  a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
- * @head: bucket to dump
+ * @cb: Control block containing additional options
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: 0 or error code.
  */
 static int
-batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
-                            struct hlist_head *head, int *idx_skip)
+batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid,
+                            struct netlink_callback *cb,
+                            struct batadv_hashtable *hash, unsigned int bucket,
+                            int *idx_skip)
 {
        struct batadv_dat_entry *dat_entry;
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(dat_entry, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(dat_entry, &hash->table[bucket], hash_entry) {
                if (idx < *idx_skip)
                        goto skip;
 
-               if (batadv_dat_cache_dump_entry(msg, portid, seq,
-                                               dat_entry)) {
-                       rcu_read_unlock();
+               if (batadv_dat_cache_dump_entry(msg, portid, cb, dat_entry)) {
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_skip = idx;
 
                        return -EMSGSIZE;
@@ -930,7 +938,7 @@ batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 skip:
                idx++;
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        return 0;
 }
@@ -951,7 +959,6 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hashtable *hash;
        struct batadv_priv *bat_priv;
        int bucket = cb->args[0];
-       struct hlist_head *head;
        int idx = cb->args[1];
        int ifindex;
        int ret = 0;
@@ -977,10 +984,7 @@ int batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
        }
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_dat_cache_dump_bucket(msg, portid,
-                                                cb->nlh->nlmsg_seq, head,
+               if (batadv_dat_cache_dump_bucket(msg, portid, cb, hash, bucket,
                                                 &idx))
                        break;
 
index 140c61a..9d8e5ed 100644 (file)
@@ -377,6 +377,7 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
 
        kref_get(&gw_node->refcount);
        hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
+       bat_priv->gw.generation++;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
@@ -472,6 +473,7 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv,
                if (!hlist_unhashed(&gw_node->list)) {
                        hlist_del_init_rcu(&gw_node->list);
                        batadv_gw_node_put(gw_node);
+                       bat_priv->gw.generation++;
                }
                spin_unlock_bh(&bat_priv->gw.list_lock);
 
@@ -518,6 +520,7 @@ void batadv_gw_node_free(struct batadv_priv *bat_priv)
                                  &bat_priv->gw.gateway_list, list) {
                hlist_del_init_rcu(&gw_node->list);
                batadv_gw_node_put(gw_node);
+               bat_priv->gw.generation++;
        }
        spin_unlock_bh(&bat_priv->gw.list_lock);
 }
index 781c5b6..508f441 100644 (file)
@@ -951,6 +951,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
        batadv_check_known_mac_addr(hard_iface->net_dev);
        kref_get(&hard_iface->refcount);
        list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
+       batadv_hardif_generation++;
 
        return hard_iface;
 
@@ -993,6 +994,7 @@ void batadv_hardif_remove_interfaces(void)
        list_for_each_entry_safe(hard_iface, hard_iface_tmp,
                                 &batadv_hardif_list, list) {
                list_del_rcu(&hard_iface->list);
+               batadv_hardif_generation++;
                batadv_hardif_remove_interface(hard_iface);
        }
        rtnl_unlock();
@@ -1054,6 +1056,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
        case NETDEV_UNREGISTER:
        case NETDEV_PRE_TYPE_CHANGE:
                list_del_rcu(&hard_iface->list);
+               batadv_hardif_generation++;
 
                batadv_hardif_remove_interface(hard_iface);
                break;
index 7b49e40..9194f4d 100644 (file)
@@ -32,6 +32,8 @@ static void batadv_hash_init(struct batadv_hashtable *hash)
                INIT_HLIST_HEAD(&hash->table[i]);
                spin_lock_init(&hash->list_locks[i]);
        }
+
+       atomic_set(&hash->generation, 0);
 }
 
 /**
index 9490a7c..0e36fa1 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "main.h"
 
+#include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
@@ -58,6 +59,9 @@ struct batadv_hashtable {
 
        /** @size: size of hashtable */
        u32 size;
+
+       /** @generation: current (generation) sequence number */
+       atomic_t generation;
 };
 
 /* allocates and clears the hash */
@@ -112,6 +116,7 @@ static inline int batadv_hash_add(struct batadv_hashtable *hash,
 
        /* no duplicate found in list, add new element */
        hlist_add_head_rcu(data_node, head);
+       atomic_inc(&hash->generation);
 
        ret = 0;
 
@@ -154,6 +159,7 @@ static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
 
                data_save = node;
                hlist_del_rcu(node);
+               atomic_inc(&hash->generation);
                break;
        }
        spin_unlock_bh(&hash->list_locks[index]);
index 6beb5f0..02e55b7 100644 (file)
@@ -43,6 +43,8 @@
 #include "debugfs.h"
 #include "trace.h"
 
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
+
 #define BATADV_LOG_BUFF_MASK (batadv_log_buff_len - 1)
 
 static const int batadv_log_buff_len = BATADV_LOG_BUF_LEN;
@@ -92,33 +94,6 @@ static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log,
        return 0;
 }
 
-/**
- * batadv_debug_log() - Add debug log entry
- * @bat_priv: the bat priv with all the soft interface information
- * @fmt: format string
- *
- * Return: 0 on success or negative error number in case of failure
- */
-int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
-{
-       struct va_format vaf;
-       va_list args;
-
-       va_start(args, fmt);
-
-       vaf.fmt = fmt;
-       vaf.va = &args;
-
-       batadv_fdebug_log(bat_priv->debug_log, "[%10u] %pV",
-                         jiffies_to_msecs(jiffies), &vaf);
-
-       trace_batadv_dbg(bat_priv, &vaf);
-
-       va_end(args);
-
-       return 0;
-}
-
 static int batadv_log_open(struct inode *inode, struct file *file)
 {
        if (!try_module_get(THIS_MODULE))
@@ -259,3 +234,34 @@ void batadv_debug_log_cleanup(struct batadv_priv *bat_priv)
        kfree(bat_priv->debug_log);
        bat_priv->debug_log = NULL;
 }
+
+#endif /* CONFIG_BATMAN_ADV_DEBUGFS */
+
+/**
+ * batadv_debug_log() - Add debug log entry
+ * @bat_priv: the bat priv with all the soft interface information
+ * @fmt: format string
+ *
+ * Return: 0 on success or negative error number in case of failure
+ */
+int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+#ifdef CONFIG_BATMAN_ADV_DEBUGFS
+       batadv_fdebug_log(bat_priv->debug_log, "[%10u] %pV",
+                         jiffies_to_msecs(jiffies), &vaf);
+#endif
+
+       trace_batadv_dbg(bat_priv, &vaf);
+
+       va_end(args);
+
+       return 0;
+}
index 69c0d85..d1ed839 100644 (file)
@@ -74,6 +74,7 @@
  * list traversals just rcu-locked
  */
 struct list_head batadv_hardif_list;
+unsigned int batadv_hardif_generation;
 static int (*batadv_rx_handler[256])(struct sk_buff *skb,
                                     struct batadv_hard_iface *recv_if);
 
@@ -186,6 +187,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
        INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
        INIT_HLIST_HEAD(&bat_priv->tp_list);
 
+       bat_priv->gw.generation = 0;
+
        ret = batadv_v_mesh_init(bat_priv);
        if (ret < 0)
                goto err;
index 2002b70..b572066 100644 (file)
@@ -25,7 +25,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2018.4"
+#define BATADV_SOURCE_VERSION "2019.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
@@ -247,6 +247,7 @@ static inline int batadv_print_vid(unsigned short vid)
 }
 
 extern struct list_head batadv_hardif_list;
+extern unsigned int batadv_hardif_generation;
 
 extern unsigned char batadv_broadcast_addr[];
 extern struct workqueue_struct *batadv_event_workqueue;
index 86725d7..69244e4 100644 (file)
@@ -1365,22 +1365,26 @@ int batadv_mcast_mesh_info_put(struct sk_buff *msg,
  *  to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @orig_node: originator to dump the multicast flags of
  *
  * Return: 0 or error code.
  */
 static int
-batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid,
+                             struct netlink_callback *cb,
                              struct batadv_orig_node *orig_node)
 {
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI, BATADV_CMD_GET_MCAST_FLAGS);
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
+                         BATADV_CMD_GET_MCAST_FLAGS);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
                    orig_node->orig)) {
                genlmsg_cancel(msg, hdr);
@@ -1405,21 +1409,26 @@ batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  *  table to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
- * @head: bucket to dump
+ * @cb: Control block containing additional options
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_skip: How many entries to skip
  *
  * Return: 0 or error code.
  */
 static int
-batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
-                              struct hlist_head *head, long *idx_skip)
+batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid,
+                              struct netlink_callback *cb,
+                              struct batadv_hashtable *hash,
+                              unsigned int bucket, long *idx_skip)
 {
        struct batadv_orig_node *orig_node;
        long idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(orig_node, &hash->table[bucket], hash_entry) {
                if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
                              &orig_node->capa_initialized))
                        continue;
@@ -1427,9 +1436,8 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
                if (idx < *idx_skip)
                        goto skip;
 
-               if (batadv_mcast_flags_dump_entry(msg, portid, seq,
-                                                 orig_node)) {
-                       rcu_read_unlock();
+               if (batadv_mcast_flags_dump_entry(msg, portid, cb, orig_node)) {
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_skip = idx;
 
                        return -EMSGSIZE;
@@ -1438,7 +1446,7 @@ batadv_mcast_flags_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
 skip:
                idx++;
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        return 0;
 }
@@ -1447,7 +1455,7 @@ skip:
  * __batadv_mcast_flags_dump() - dump multicast flags table to a netlink socket
  * @msg: buffer for the message
  * @portid: netlink port
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: the bat priv with all the soft interface information
  * @bucket: current bucket to dump
  * @idx: index in current bucket to the next entry to dump
@@ -1455,19 +1463,17 @@ skip:
  * Return: 0 or error code.
  */
 static int
-__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid, u32 seq,
+__batadv_mcast_flags_dump(struct sk_buff *msg, u32 portid,
+                         struct netlink_callback *cb,
                          struct batadv_priv *bat_priv, long *bucket, long *idx)
 {
        struct batadv_hashtable *hash = bat_priv->orig_hash;
        long bucket_tmp = *bucket;
-       struct hlist_head *head;
        long idx_tmp = *idx;
 
        while (bucket_tmp < hash->size) {
-               head = &hash->table[bucket_tmp];
-
-               if (batadv_mcast_flags_dump_bucket(msg, portid, seq, head,
-                                                  &idx_tmp))
+               if (batadv_mcast_flags_dump_bucket(msg, portid, cb, hash,
+                                                  *bucket, &idx_tmp))
                        break;
 
                bucket_tmp++;
@@ -1550,8 +1556,7 @@ int batadv_mcast_flags_dump(struct sk_buff *msg, struct netlink_callback *cb)
                return ret;
 
        bat_priv = netdev_priv(primary_if->soft_iface);
-       ret = __batadv_mcast_flags_dump(msg, portid, cb->nlh->nlmsg_seq,
-                                       bat_priv, bucket, idx);
+       ret = __batadv_mcast_flags_dump(msg, portid, cb, bat_priv, bucket, idx);
 
        batadv_hardif_put(primary_if);
        return ret;
index 0d9459b..2dc3304 100644 (file)
 #include <linux/if_ether.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
 #include <linux/printk.h>
-#include <linux/rculist.h>
-#include <linux/rcupdate.h>
+#include <linux/rtnetlink.h>
 #include <linux/skbuff.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
@@ -445,23 +445,27 @@ out:
  * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @hard_iface: Hard interface to dump
  *
  * Return: error code, or 0 on success
  */
 static int
-batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid,
+                                struct netlink_callback *cb,
                                 struct batadv_hard_iface *hard_iface)
 {
        struct net_device *net_dev = hard_iface->net_dev;
        void *hdr;
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family, NLM_F_MULTI,
                          BATADV_CMD_GET_HARDIFS);
        if (!hdr)
                return -EMSGSIZE;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
                        net_dev->ifindex) ||
            nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
@@ -498,7 +502,6 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_hard_iface *hard_iface;
        int ifindex;
        int portid = NETLINK_CB(cb->skb).portid;
-       int seq = cb->nlh->nlmsg_seq;
        int skip = cb->args[0];
        int i = 0;
 
@@ -516,23 +519,24 @@ batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
                return -ENODEV;
        }
 
-       rcu_read_lock();
+       rtnl_lock();
+       cb->seq = batadv_hardif_generation << 1 | 1;
 
-       list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
+       list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
                if (hard_iface->soft_iface != soft_iface)
                        continue;
 
                if (i++ < skip)
                        continue;
 
-               if (batadv_netlink_dump_hardif_entry(msg, portid, seq,
+               if (batadv_netlink_dump_hardif_entry(msg, portid, cb,
                                                     hard_iface)) {
                        i--;
                        break;
                }
        }
 
-       rcu_read_unlock();
+       rtnl_unlock();
 
        dev_put(soft_iface);
 
index 3d57f99..8e10242 100644 (file)
@@ -16,7 +16,5 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <linux/module.h>
-
 #define CREATE_TRACE_POINTS
 #include "trace.h"
index 3acda26..104784b 100644 (file)
 
 #include "main.h"
 
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
 #include <linux/tracepoint.h>
+#include <linux/types.h>
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM batadv
index d21624c..8dcd496 100644 (file)
@@ -1145,14 +1145,15 @@ out:
  * batadv_tt_local_dump_entry() - Dump one TT local entry into a message
  * @msg :Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
  * @common: tt local & tt global common data
  *
  * Return: Error code, or 0 on success
  */
 static int
-batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
+                          struct netlink_callback *cb,
                           struct batadv_priv *bat_priv,
                           struct batadv_tt_common_entry *common)
 {
@@ -1173,12 +1174,14 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
 
        batadv_softif_vlan_put(vlan);
 
-       hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
-                         NLM_F_MULTI,
+       hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
+                         &batadv_netlink_family,  NLM_F_MULTI,
                          BATADV_CMD_GET_TRANSTABLE_LOCAL);
        if (!hdr)
                return -ENOBUFS;
 
+       genl_dump_check_consistent(cb, hdr);
+
        if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
            nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
            nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
@@ -1201,34 +1204,39 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
  * batadv_tt_local_dump_bucket() - Dump one TT local bucket into a message
  * @msg: Netlink message to dump into
  * @portid: Port making netlink request
- * @seq: Sequence number of netlink message
+ * @cb: Control block containing additional options
  * @bat_priv: The bat priv with all the soft interface information
- * @head: Pointer to the list containing the local tt entries
+ * @hash: hash to dump
+ * @bucket: bucket index to dump
  * @idx_s: Number of entries to skip
  *
  * Return: Error code, or 0 on success
  */
 static int
-batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
+batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid,
+                           struct netlink_callback *cb,
                            struct batadv_priv *bat_priv,
-                           struct hlist_head *head, int *idx_s)
+                           struct batadv_hashtable *hash, unsigned int bucket,
+                           int *idx_s)
 {
        struct batadv_tt_common_entry *common;
        int idx = 0;
 
-       rcu_read_lock();
-       hlist_for_each_entry_rcu(common, head, hash_entry) {
+       spin_lock_bh(&hash->list_locks[bucket]);
+       cb->seq = atomic_read(&hash->generation) << 1 | 1;
+
+       hlist_for_each_entry(common, &hash->table[bucket], hash_entry) {
                if (idx++ < *idx_s)
                        continue;
 
-               if (batadv_tt_local_dump_entry(msg, portid, seq, bat_priv,
+               if (batadv_tt_local_dump_entry(msg, portid, cb, bat_priv,
                                               common)) {
-                       rcu_read_unlock();
+                       spin_unlock_bh(&hash->list_locks[bucket]);
                        *idx_s = idx - 1;
                        return -EMSGSIZE;
                }
        }
-       rcu_read_unlock();
+       spin_unlock_bh(&hash->list_locks[bucket]);
 
        *idx_s = 0;
        return 0;
@@ -1248,7 +1256,6 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
        struct batadv_priv *bat_priv;
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_hashtable *hash;
-       struct hlist_head *head;
        int ret;
        int ifindex;
        int bucket = cb->args[0];
@@ -1276,10 +1283,8 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
        hash = bat_priv->tt.local_hash;
 
        while (bucket < hash->size) {
-               head = &hash->table[bucket];
-
-               if (batadv_tt_local_dump_bucket(msg, portid, cb->nlh->nlmsg_seq,
-                                               bat_priv, head, &idx))
+               if (batadv_tt_local_dump_bucket(msg, portid, cb, bat_priv,
+                                               hash, bucket, &idx))
                        break;
 
                bucket++;
index 45b5592..cbe17da 100644 (file)
@@ -1096,12 +1096,15 @@ struct batadv_priv_gw {
        /** @gateway_list: list of available gateway nodes */
        struct hlist_head gateway_list;
 
-       /** @list_lock: lock protecting gateway_list & curr_gw */
+       /** @list_lock: lock protecting gateway_list, curr_gw, generation */
        spinlock_t list_lock;
 
        /** @curr_gw: pointer to currently selected gateway node */
        struct batadv_gw_node __rcu *curr_gw;
 
+       /** @generation: current (generation) sequence number */
+       unsigned int generation;
+
        /**
         * @mode: gateway operation: off, client or server (see batadv_gw_modes)
         */
index 828e87f..9d79c7d 100644 (file)
@@ -607,7 +607,7 @@ static void ifup(struct net_device *netdev)
        int err;
 
        rtnl_lock();
-       err = dev_open(netdev);
+       err = dev_open(netdev, NULL);
        if (err < 0)
                BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
        rtnl_unlock();
index 360ad66..4e7cd99 100644 (file)
@@ -175,6 +175,82 @@ static struct notifier_block br_switchdev_notifier = {
        .notifier_call = br_switchdev_event,
 };
 
+/* br_boolopt_toggle - change user-controlled boolean option
+ *
+ * @br: bridge device
+ * @opt: id of the option to change
+ * @on: new option value
+ * @extack: extack for error messages
+ *
+ * Changes the value of the respective boolean option to @on taking care of
+ * any internal option value mapping and configuration.
+ */
+int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
+                     struct netlink_ext_ack *extack)
+{
+       switch (opt) {
+       case BR_BOOLOPT_NO_LL_LEARN:
+               br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
+               break;
+       default:
+               /* shouldn't be called with unsupported options */
+               WARN_ON(1);
+               break;
+       }
+
+       return 0;
+}
+
+int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
+{
+       switch (opt) {
+       case BR_BOOLOPT_NO_LL_LEARN:
+               return br_opt_get(br, BROPT_NO_LL_LEARN);
+       default:
+               /* shouldn't be called with unsupported options */
+               WARN_ON(1);
+               break;
+       }
+
+       return 0;
+}
+
+int br_boolopt_multi_toggle(struct net_bridge *br,
+                           struct br_boolopt_multi *bm,
+                           struct netlink_ext_ack *extack)
+{
+       unsigned long bitmap = bm->optmask;
+       int err = 0;
+       int opt_id;
+
+       for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
+               bool on = !!(bm->optval & BIT(opt_id));
+
+               err = br_boolopt_toggle(br, opt_id, on, extack);
+               if (err) {
+                       br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
+                                opt_id, br_boolopt_get(br, opt_id), on, err);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+void br_boolopt_multi_get(const struct net_bridge *br,
+                         struct br_boolopt_multi *bm)
+{
+       u32 optval = 0;
+       int opt_id;
+
+       for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
+               optval |= (br_boolopt_get(br, opt_id) << opt_id);
+
+       bm->optval = optval;
+       bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
+}
+
+/* private bridge options, controlled by the kernel */
 void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
 {
        bool cur = !!br_opt_get(br, opt);
index c6abf92..9f41a5d 100644 (file)
@@ -131,9 +131,17 @@ static int br_dev_init(struct net_device *dev)
                return err;
        }
 
+       err = br_mdb_hash_init(br);
+       if (err) {
+               free_percpu(br->stats);
+               br_fdb_hash_fini(br);
+               return err;
+       }
+
        err = br_vlan_init(br);
        if (err) {
                free_percpu(br->stats);
+               br_mdb_hash_fini(br);
                br_fdb_hash_fini(br);
                return err;
        }
@@ -142,6 +150,7 @@ static int br_dev_init(struct net_device *dev)
        if (err) {
                free_percpu(br->stats);
                br_vlan_flush(br);
+               br_mdb_hash_fini(br);
                br_fdb_hash_fini(br);
        }
        br_set_lockdep_class(dev);
@@ -156,6 +165,7 @@ static void br_dev_uninit(struct net_device *dev)
        br_multicast_dev_del(br);
        br_multicast_uninit_stats(br);
        br_vlan_flush(br);
+       br_mdb_hash_fini(br);
        br_fdb_hash_fini(br);
        free_percpu(br->stats);
 }
index e56ba39..38b1d0d 100644 (file)
@@ -1164,3 +1164,23 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
 
        spin_unlock_bh(&br->hash_lock);
 }
+
+void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
+{
+       struct net_bridge_fdb_entry *f;
+       struct net_bridge_port *p;
+
+       ASSERT_RTNL();
+
+       p = br_port_get_rtnl(dev);
+       if (!p)
+               return;
+
+       spin_lock_bh(&p->br->hash_lock);
+       hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
+               if (f->dst == p && f->key.vlan_id == vid)
+                       f->offloaded = 0;
+       }
+       spin_unlock_bh(&p->br->hash_lock);
+}
+EXPORT_SYMBOL_GPL(br_fdb_clear_offload);
index 9b46d2d..d4863f5 100644 (file)
@@ -741,3 +741,15 @@ void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
        if (mask & BR_NEIGH_SUPPRESS)
                br_recalculate_neigh_suppress_enabled(br);
 }
+
+bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+{
+       struct net_bridge_port *p;
+
+       p = br_port_get_rtnl_rcu(dev);
+       if (!p)
+               return false;
+
+       return p->flags & flag;
+}
+EXPORT_SYMBOL_GPL(br_port_flag_is_set);
index 3ddca11..5ea7e56 100644 (file)
@@ -188,7 +188,9 @@ static void __br_handle_local_finish(struct sk_buff *skb)
        u16 vid = 0;
 
        /* check if vlan is allowed, to avoid spoofing */
-       if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
+       if ((p->flags & BR_LEARNING) &&
+           !br_opt_get(p->br, BROPT_NO_LL_LEARN) &&
+           br_should_learn(p, skb, &vid))
                br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
 }
 
index a7ea2d4..79d4c9d 100644 (file)
@@ -78,82 +78,72 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
 static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
                            struct net_device *dev)
 {
+       int idx = 0, s_idx = cb->args[1], err = 0;
        struct net_bridge *br = netdev_priv(dev);
-       struct net_bridge_mdb_htable *mdb;
+       struct net_bridge_mdb_entry *mp;
        struct nlattr *nest, *nest2;
-       int i, err = 0;
-       int idx = 0, s_idx = cb->args[1];
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
                return 0;
 
-       mdb = rcu_dereference(br->mdb);
-       if (!mdb)
-               return 0;
-
        nest = nla_nest_start(skb, MDBA_MDB);
        if (nest == NULL)
                return -EMSGSIZE;
 
-       for (i = 0; i < mdb->max; i++) {
-               struct net_bridge_mdb_entry *mp;
+       hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
                struct net_bridge_port_group *p;
                struct net_bridge_port_group __rcu **pp;
                struct net_bridge_port *port;
 
-               hlist_for_each_entry_rcu(mp, &mdb->mhash[i], hlist[mdb->ver]) {
-                       if (idx < s_idx)
-                               goto skip;
+               if (idx < s_idx)
+                       goto skip;
 
-                       nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
-                       if (nest2 == NULL) {
-                               err = -EMSGSIZE;
-                               goto out;
-                       }
+               nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
+               if (!nest2) {
+                       err = -EMSGSIZE;
+                       break;
+               }
 
-                       for (pp = &mp->ports;
-                            (p = rcu_dereference(*pp)) != NULL;
-                             pp = &p->next) {
-                               struct nlattr *nest_ent;
-                               struct br_mdb_entry e;
-
-                               port = p->port;
-                               if (!port)
-                                       continue;
-
-                               memset(&e, 0, sizeof(e));
-                               e.ifindex = port->dev->ifindex;
-                               e.vid = p->addr.vid;
-                               __mdb_entry_fill_flags(&e, p->flags);
-                               if (p->addr.proto == htons(ETH_P_IP))
-                                       e.addr.u.ip4 = p->addr.u.ip4;
+               for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
+                     pp = &p->next) {
+                       struct nlattr *nest_ent;
+                       struct br_mdb_entry e;
+
+                       port = p->port;
+                       if (!port)
+                               continue;
+
+                       memset(&e, 0, sizeof(e));
+                       e.ifindex = port->dev->ifindex;
+                       e.vid = p->addr.vid;
+                       __mdb_entry_fill_flags(&e, p->flags);
+                       if (p->addr.proto == htons(ETH_P_IP))
+                               e.addr.u.ip4 = p->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
-                               if (p->addr.proto == htons(ETH_P_IPV6))
-                                       e.addr.u.ip6 = p->addr.u.ip6;
+                       if (p->addr.proto == htons(ETH_P_IPV6))
+                               e.addr.u.ip6 = p->addr.u.ip6;
 #endif
-                               e.addr.proto = p->addr.proto;
-                               nest_ent = nla_nest_start(skb,
-                                                         MDBA_MDB_ENTRY_INFO);
-                               if (!nest_ent) {
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               if (nla_put_nohdr(skb, sizeof(e), &e) ||
-                                   nla_put_u32(skb,
-                                               MDBA_MDB_EATTR_TIMER,
-                                               br_timer_value(&p->timer))) {
-                                       nla_nest_cancel(skb, nest_ent);
-                                       nla_nest_cancel(skb, nest2);
-                                       err = -EMSGSIZE;
-                                       goto out;
-                               }
-                               nla_nest_end(skb, nest_ent);
+                       e.addr.proto = p->addr.proto;
+                       nest_ent = nla_nest_start(skb, MDBA_MDB_ENTRY_INFO);
+                       if (!nest_ent) {
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
                        }
-                       nla_nest_end(skb, nest2);
-               skip:
-                       idx++;
+                       if (nla_put_nohdr(skb, sizeof(e), &e) ||
+                           nla_put_u32(skb,
+                                       MDBA_MDB_EATTR_TIMER,
+                                       br_timer_value(&p->timer))) {
+                               nla_nest_cancel(skb, nest_ent);
+                               nla_nest_cancel(skb, nest2);
+                               err = -EMSGSIZE;
+                               goto out;
+                       }
+                       nla_nest_end(skb, nest_ent);
                }
+               nla_nest_end(skb, nest2);
+skip:
+               idx++;
        }
 
 out:
@@ -203,8 +193,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
 
-       /* In theory this could be wrapped to 0... */
-       cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
+       cb->seq = net->dev_base_seq;
 
        for_each_netdev_rcu(net, dev) {
                if (dev->priv_flags & IFF_EBRIDGE) {
@@ -297,7 +286,6 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
        struct br_mdb_complete_info *data = priv;
        struct net_bridge_port_group __rcu **pp;
        struct net_bridge_port_group *p;
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port *port = data->port;
        struct net_bridge *br = port->br;
@@ -306,8 +294,7 @@ static void br_mdb_complete(struct net_device *dev, int err, void *priv)
                goto err;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, &data->ip);
+       mp = br_mdb_ip_get(br, &data->ip);
        if (!mp)
                goto out;
        for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
@@ -588,14 +575,12 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
-       struct net_bridge_mdb_htable *mdb;
        unsigned long now = jiffies;
        int err;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp) {
-               mp = br_multicast_new_group(br, port, group);
+               mp = br_multicast_new_group(br, group);
                err = PTR_ERR_OR_ZERO(mp);
                if (err)
                        return err;
@@ -696,7 +681,6 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
 
 static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
@@ -709,9 +693,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
        __mdb_entry_to_br_ip(entry, &ip);
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &ip);
+       mp = br_mdb_ip_get(br, &ip);
        if (!mp)
                goto unlock;
 
@@ -728,7 +710,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
                rcu_assign_pointer(*pp, p->next);
                hlist_del_init(&p->mglist);
                del_timer(&p->timer);
-               call_rcu_bh(&p->rcu, br_multicast_free_pg);
+               kfree_rcu(p, rcu);
                err = 0;
 
                if (!mp->ports && !mp->host_joined &&
index 6bac0d6..879cd23 100644 (file)
 
 #include "br_private.h"
 
+static const struct rhashtable_params br_mdb_rht_params = {
+       .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
+       .key_offset = offsetof(struct net_bridge_mdb_entry, addr),
+       .key_len = sizeof(struct br_ip),
+       .automatic_shrinking = true,
+       .locks_mul = 1,
+};
+
 static void br_multicast_start_querier(struct net_bridge *br,
                                       struct bridge_mcast_own_query *query);
 static void br_multicast_add_router(struct net_bridge *br,
@@ -54,7 +62,6 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
                                         const struct in6_addr *group,
                                         __u16 vid, const unsigned char *src);
 #endif
-unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 {
@@ -73,89 +80,58 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
        return 0;
 }
 
-static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip,
-                               __u16 vid)
-{
-       return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1);
-}
-
-#if IS_ENABLED(CONFIG_IPV6)
-static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb,
-                               const struct in6_addr *ip,
-                               __u16 vid)
-{
-       return jhash_2words(ipv6_addr_hash(ip), vid,
-                           mdb->secret) & (mdb->max - 1);
-}
-#endif
-
-static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb,
-                            struct br_ip *ip)
+static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
+                                                     struct br_ip *dst)
 {
-       switch (ip->proto) {
-       case htons(ETH_P_IP):
-               return __br_ip4_hash(mdb, ip->u.ip4, ip->vid);
-#if IS_ENABLED(CONFIG_IPV6)
-       case htons(ETH_P_IPV6):
-               return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid);
-#endif
-       }
-       return 0;
+       return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
 }
 
-static struct net_bridge_mdb_entry *__br_mdb_ip_get(
-       struct net_bridge_mdb_htable *mdb, struct br_ip *dst, int hash)
+struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
+                                          struct br_ip *dst)
 {
-       struct net_bridge_mdb_entry *mp;
+       struct net_bridge_mdb_entry *ent;
 
-       hlist_for_each_entry_rcu(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               if (br_ip_equal(&mp->addr, dst))
-                       return mp;
-       }
-
-       return NULL;
-}
+       lockdep_assert_held_once(&br->multicast_lock);
 
-struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
-                                          struct br_ip *dst)
-{
-       if (!mdb)
-               return NULL;
+       rcu_read_lock();
+       ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
+       rcu_read_unlock();
 
-       return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst));
+       return ent;
 }
 
-static struct net_bridge_mdb_entry *br_mdb_ip4_get(
-       struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
+                                                  __be32 dst, __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip4 = dst;
        br_dst.proto = htons(ETH_P_IP);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static struct net_bridge_mdb_entry *br_mdb_ip6_get(
-       struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst,
-       __u16 vid)
+static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
+                                                  const struct in6_addr *dst,
+                                                  __u16 vid)
 {
        struct br_ip br_dst;
 
+       memset(&br_dst, 0, sizeof(br_dst));
        br_dst.u.ip6 = *dst;
        br_dst.proto = htons(ETH_P_IPV6);
        br_dst.vid = vid;
 
-       return br_mdb_ip_get(mdb, &br_dst);
+       return br_mdb_ip_get(br, &br_dst);
 }
 #endif
 
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                                        struct sk_buff *skb, u16 vid)
 {
-       struct net_bridge_mdb_htable *mdb = rcu_dereference(br->mdb);
        struct br_ip ip;
 
        if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
@@ -164,6 +140,7 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
        if (BR_INPUT_SKB_CB(skb)->igmp)
                return NULL;
 
+       memset(&ip, 0, sizeof(ip));
        ip.proto = skb->protocol;
        ip.vid = vid;
 
@@ -180,70 +157,13 @@ struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
                return NULL;
        }
 
-       return br_mdb_ip_get(mdb, &ip);
-}
-
-static void br_mdb_free(struct rcu_head *head)
-{
-       struct net_bridge_mdb_htable *mdb =
-               container_of(head, struct net_bridge_mdb_htable, rcu);
-       struct net_bridge_mdb_htable *old = mdb->old;
-
-       mdb->old = NULL;
-       kfree(old->mhash);
-       kfree(old);
-}
-
-static int br_mdb_copy(struct net_bridge_mdb_htable *new,
-                      struct net_bridge_mdb_htable *old,
-                      int elasticity)
-{
-       struct net_bridge_mdb_entry *mp;
-       int maxlen;
-       int len;
-       int i;
-
-       for (i = 0; i < old->max; i++)
-               hlist_for_each_entry(mp, &old->mhash[i], hlist[old->ver])
-                       hlist_add_head(&mp->hlist[new->ver],
-                                      &new->mhash[br_ip_hash(new, &mp->addr)]);
-
-       if (!elasticity)
-               return 0;
-
-       maxlen = 0;
-       for (i = 0; i < new->max; i++) {
-               len = 0;
-               hlist_for_each_entry(mp, &new->mhash[i], hlist[new->ver])
-                       len++;
-               if (len > maxlen)
-                       maxlen = len;
-       }
-
-       return maxlen > elasticity ? -EINVAL : 0;
-}
-
-void br_multicast_free_pg(struct rcu_head *head)
-{
-       struct net_bridge_port_group *p =
-               container_of(head, struct net_bridge_port_group, rcu);
-
-       kfree(p);
-}
-
-static void br_multicast_free_group(struct rcu_head *head)
-{
-       struct net_bridge_mdb_entry *mp =
-               container_of(head, struct net_bridge_mdb_entry, rcu);
-
-       kfree(mp);
+       return br_mdb_ip_get_rcu(br, &ip);
 }
 
 static void br_multicast_group_expired(struct timer_list *t)
 {
        struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
        struct net_bridge *br = mp->br;
-       struct net_bridge_mdb_htable *mdb;
 
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || timer_pending(&mp->timer))
@@ -255,12 +175,11 @@ static void br_multicast_group_expired(struct timer_list *t)
        if (mp->ports)
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
+       rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                              br_mdb_rht_params);
+       hlist_del_rcu(&mp->mdb_node);
 
-       hlist_del_rcu(&mp->hlist[mdb->ver]);
-       mdb->size--;
-
-       call_rcu_bh(&mp->rcu, br_multicast_free_group);
+       kfree_rcu(mp, rcu);
 
 out:
        spin_unlock(&br->multicast_lock);
@@ -269,14 +188,11 @@ out:
 static void br_multicast_del_pg(struct net_bridge *br,
                                struct net_bridge_port_group *pg)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
 
-       mdb = mlock_dereference(br->mdb, br);
-
-       mp = br_mdb_ip_get(mdb, &pg->addr);
+       mp = br_mdb_ip_get(br, &pg->addr);
        if (WARN_ON(!mp))
                return;
 
@@ -291,7 +207,7 @@ static void br_multicast_del_pg(struct net_bridge *br,
                del_timer(&p->timer);
                br_mdb_notify(br->dev, p->port, &pg->addr, RTM_DELMDB,
                              p->flags);
-               call_rcu_bh(&p->rcu, br_multicast_free_pg);
+               kfree_rcu(p, rcu);
 
                if (!mp->ports && !mp->host_joined &&
                    netif_running(br->dev))
@@ -319,53 +235,6 @@ out:
        spin_unlock(&br->multicast_lock);
 }
 
-static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max,
-                        int elasticity)
-{
-       struct net_bridge_mdb_htable *old = rcu_dereference_protected(*mdbp, 1);
-       struct net_bridge_mdb_htable *mdb;
-       int err;
-
-       mdb = kmalloc(sizeof(*mdb), GFP_ATOMIC);
-       if (!mdb)
-               return -ENOMEM;
-
-       mdb->max = max;
-       mdb->old = old;
-
-       mdb->mhash = kcalloc(max, sizeof(*mdb->mhash), GFP_ATOMIC);
-       if (!mdb->mhash) {
-               kfree(mdb);
-               return -ENOMEM;
-       }
-
-       mdb->size = old ? old->size : 0;
-       mdb->ver = old ? old->ver ^ 1 : 0;
-
-       if (!old || elasticity)
-               get_random_bytes(&mdb->secret, sizeof(mdb->secret));
-       else
-               mdb->secret = old->secret;
-
-       if (!old)
-               goto out;
-
-       err = br_mdb_copy(mdb, old, elasticity);
-       if (err) {
-               kfree(mdb->mhash);
-               kfree(mdb);
-               return err;
-       }
-
-       br_mdb_rehash_seq++;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
-       rcu_assign_pointer(*mdbp, mdb);
-
-       return 0;
-}
-
 static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
                                                    __be32 group,
                                                    u8 *igmp_type)
@@ -589,111 +458,19 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
        return NULL;
 }
 
-static struct net_bridge_mdb_entry *br_multicast_get_group(
-       struct net_bridge *br, struct net_bridge_port *port,
-       struct br_ip *group, int hash)
-{
-       struct net_bridge_mdb_htable *mdb;
-       struct net_bridge_mdb_entry *mp;
-       unsigned int count = 0;
-       unsigned int max;
-       int elasticity;
-       int err;
-
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       hlist_for_each_entry(mp, &mdb->mhash[hash], hlist[mdb->ver]) {
-               count++;
-               if (unlikely(br_ip_equal(group, &mp->addr)))
-                       return mp;
-       }
-
-       elasticity = 0;
-       max = mdb->max;
-
-       if (unlikely(count > br->hash_elasticity && count)) {
-               if (net_ratelimit())
-                       br_info(br, "Multicast hash table "
-                               "chain limit reached: %s\n",
-                               port ? port->dev->name : br->dev->name);
-
-               elasticity = br->hash_elasticity;
-       }
-
-       if (mdb->size >= max) {
-               max *= 2;
-               if (unlikely(max > br->hash_max)) {
-                       br_warn(br, "Multicast hash table maximum of %d "
-                               "reached, disabling snooping: %s\n",
-                               br->hash_max,
-                               port ? port->dev->name : br->dev->name);
-                       err = -E2BIG;
-disable:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto err;
-               }
-       }
-
-       if (max > mdb->max || elasticity) {
-               if (mdb->old) {
-                       if (net_ratelimit())
-                               br_info(br, "Multicast hash table "
-                                       "on fire: %s\n",
-                                       port ? port->dev->name : br->dev->name);
-                       err = -EEXIST;
-                       goto err;
-               }
-
-               err = br_mdb_rehash(&br->mdb, max, elasticity);
-               if (err) {
-                       br_warn(br, "Cannot rehash multicast "
-                               "hash table, disabling snooping: %s, %d, %d\n",
-                               port ? port->dev->name : br->dev->name,
-                               mdb->size, err);
-                       goto disable;
-               }
-
-               err = -EAGAIN;
-               goto err;
-       }
-
-       return NULL;
-
-err:
-       mp = ERR_PTR(err);
-       return mp;
-}
-
 struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
-                                                   struct net_bridge_port *p,
                                                    struct br_ip *group)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       int hash;
        int err;
 
-       mdb = rcu_dereference_protected(br->mdb, 1);
-       if (!mdb) {
-               err = br_mdb_rehash(&br->mdb, BR_HASH_SIZE, 0);
-               if (err)
-                       return ERR_PTR(err);
-               goto rehash;
-       }
-
-       hash = br_ip_hash(mdb, group);
-       mp = br_multicast_get_group(br, p, group, hash);
-       switch (PTR_ERR(mp)) {
-       case 0:
-               break;
-
-       case -EAGAIN:
-rehash:
-               mdb = rcu_dereference_protected(br->mdb, 1);
-               hash = br_ip_hash(mdb, group);
-               break;
+       mp = br_mdb_ip_get(br, group);
+       if (mp)
+               return mp;
 
-       default:
-               goto out;
+       if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
+               br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
+               return ERR_PTR(-E2BIG);
        }
 
        mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
@@ -703,11 +480,15 @@ rehash:
        mp->br = br;
        mp->addr = *group;
        timer_setup(&mp->timer, br_multicast_group_expired, 0);
+       err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                           br_mdb_rht_params);
+       if (err) {
+               kfree(mp);
+               mp = ERR_PTR(err);
+       } else {
+               hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
+       }
 
-       hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]);
-       mdb->size++;
-
-out:
        return mp;
 }
 
@@ -768,7 +549,7 @@ static int br_multicast_add_group(struct net_bridge *br,
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mp = br_multicast_new_group(br, port, group);
+       mp = br_multicast_new_group(br, group);
        err = PTR_ERR(mp);
        if (IS_ERR(mp))
                goto err;
@@ -837,6 +618,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br,
        if (ipv6_addr_is_ll_all_nodes(group))
                return 0;
 
+       memset(&br_group, 0, sizeof(br_group));
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
@@ -1483,7 +1265,7 @@ static void br_ip4_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip4_get(br, group, vid);
        if (!mp)
                goto out;
 
@@ -1567,7 +1349,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
+       mp = br_mdb_ip6_get(br, group, vid);
        if (!mp)
                goto out;
 
@@ -1601,7 +1383,6 @@ br_multicast_leave_group(struct net_bridge *br,
                         struct bridge_mcast_own_query *own_query,
                         const unsigned char *src)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
        struct net_bridge_port_group *p;
        unsigned long now;
@@ -1612,8 +1393,7 @@ br_multicast_leave_group(struct net_bridge *br,
            (port && port->state == BR_STATE_DISABLED))
                goto out;
 
-       mdb = mlock_dereference(br->mdb, br);
-       mp = br_mdb_ip_get(mdb, group);
+       mp = br_mdb_ip_get(br, group);
        if (!mp)
                goto out;
 
@@ -1629,7 +1409,7 @@ br_multicast_leave_group(struct net_bridge *br,
                        rcu_assign_pointer(*pp, p->next);
                        hlist_del_init(&p->mglist);
                        del_timer(&p->timer);
-                       call_rcu_bh(&p->rcu, br_multicast_free_pg);
+                       kfree_rcu(p, rcu);
                        br_mdb_notify(br->dev, port, group, RTM_DELMDB,
                                      p->flags);
 
@@ -1961,8 +1741,7 @@ static void br_ip6_multicast_query_expired(struct timer_list *t)
 
 void br_multicast_init(struct net_bridge *br)
 {
-       br->hash_elasticity = 4;
-       br->hash_max = 512;
+       br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
 
        br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
        br->multicast_last_member_count = 2;
@@ -1999,6 +1778,7 @@ void br_multicast_init(struct net_bridge *br)
        timer_setup(&br->ip6_own_query.timer,
                    br_ip6_multicast_query_expired, 0);
 #endif
+       INIT_HLIST_HEAD(&br->mdb_list);
 }
 
 static void __br_multicast_open(struct net_bridge *br,
@@ -2033,40 +1813,20 @@ void br_multicast_stop(struct net_bridge *br)
 
 void br_multicast_dev_del(struct net_bridge *br)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
-       struct hlist_node *n;
-       u32 ver;
-       int i;
+       struct hlist_node *tmp;
 
        spin_lock_bh(&br->multicast_lock);
-       mdb = mlock_dereference(br->mdb, br);
-       if (!mdb)
-               goto out;
-
-       br->mdb = NULL;
-
-       ver = mdb->ver;
-       for (i = 0; i < mdb->max; i++) {
-               hlist_for_each_entry_safe(mp, n, &mdb->mhash[i],
-                                         hlist[ver]) {
-                       del_timer(&mp->timer);
-                       call_rcu_bh(&mp->rcu, br_multicast_free_group);
-               }
+       hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node) {
+               del_timer(&mp->timer);
+               rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
+                                      br_mdb_rht_params);
+               hlist_del_rcu(&mp->mdb_node);
+               kfree_rcu(mp, rcu);
        }
-
-       if (mdb->old) {
-               spin_unlock_bh(&br->multicast_lock);
-               rcu_barrier_bh();
-               spin_lock_bh(&br->multicast_lock);
-               WARN_ON(mdb->old);
-       }
-
-       mdb->old = mdb;
-       call_rcu_bh(&mdb->rcu, br_mdb_free);
-
-out:
        spin_unlock_bh(&br->multicast_lock);
+
+       rcu_barrier();
 }
 
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
@@ -2176,7 +1936,6 @@ static void br_multicast_start_querier(struct net_bridge *br,
 
 int br_multicast_toggle(struct net_bridge *br, unsigned long val)
 {
-       struct net_bridge_mdb_htable *mdb;
        struct net_bridge_port *port;
        int err = 0;
 
@@ -2192,21 +1951,6 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)
        if (!netif_running(br->dev))
                goto unlock;
 
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, mdb->max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
        br_multicast_open(br);
        list_for_each_entry(port, &br->port_list, list)
                __br_multicast_enable_port(port);
@@ -2271,45 +2015,6 @@ unlock:
        return 0;
 }
 
-int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
-{
-       int err = -EINVAL;
-       u32 old;
-       struct net_bridge_mdb_htable *mdb;
-
-       spin_lock_bh(&br->multicast_lock);
-       if (!is_power_of_2(val))
-               goto unlock;
-
-       mdb = mlock_dereference(br->mdb, br);
-       if (mdb && val < mdb->size)
-               goto unlock;
-
-       err = 0;
-
-       old = br->hash_max;
-       br->hash_max = val;
-
-       if (mdb) {
-               if (mdb->old) {
-                       err = -EEXIST;
-rollback:
-                       br->hash_max = old;
-                       goto unlock;
-               }
-
-               err = br_mdb_rehash(&br->mdb, br->hash_max,
-                                   br->hash_elasticity);
-               if (err)
-                       goto rollback;
-       }
-
-unlock:
-       spin_unlock_bh(&br->multicast_lock);
-
-       return err;
-}
-
 int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
 {
        /* Currently we support only version 2 and 3 */
@@ -2646,3 +2351,13 @@ void br_multicast_get_stats(const struct net_bridge *br,
        }
        memcpy(dest, &tdst, sizeof(*dest));
 }
+
+int br_mdb_hash_init(struct net_bridge *br)
+{
+       return rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
+}
+
+void br_mdb_hash_fini(struct net_bridge *br)
+{
+       rhashtable_destroy(&br->mdb_hash_tbl);
+}
index b1b5e85..c9383c4 100644 (file)
@@ -671,10 +671,8 @@ static int br_nf_push_frag_xmit(struct net *net, struct sock *sk, struct sk_buff
                return 0;
        }
 
-       if (data->vlan_tci) {
-               skb->vlan_tci = data->vlan_tci;
-               skb->vlan_proto = data->vlan_proto;
-       }
+       if (data->vlan_proto)
+               __vlan_hwaccel_put_tag(skb, data->vlan_proto, data->vlan_tci);
 
        skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
        __skb_push(skb, data->encap_size);
@@ -740,8 +738,13 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff
 
                data = this_cpu_ptr(&brnf_frag_data_storage);
 
-               data->vlan_tci = skb->vlan_tci;
-               data->vlan_proto = skb->vlan_proto;
+               if (skb_vlan_tag_present(skb)) {
+                       data->vlan_tci = skb->vlan_tci;
+                       data->vlan_proto = skb->vlan_proto;
+               } else {
+                       data->vlan_proto = 0;
+               }
+
                data->encap_size = nf_bridge_encap_header_len(skb);
                data->size = ETH_HLEN + data->encap_size;
 
index 3345f19..ff2c10d 100644 (file)
@@ -1035,6 +1035,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
        [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
        [IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
        [IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
+       [IFLA_BR_MULTI_BOOLOPT] = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_boolopt_multi) },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1187,19 +1189,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
                        return err;
        }
 
-       if (data[IFLA_BR_MCAST_HASH_ELASTICITY]) {
-               u32 val = nla_get_u32(data[IFLA_BR_MCAST_HASH_ELASTICITY]);
+       if (data[IFLA_BR_MCAST_HASH_ELASTICITY])
+               br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
+                       RHT_ELASTICITY);
 
-               br->hash_elasticity = val;
-       }
-
-       if (data[IFLA_BR_MCAST_HASH_MAX]) {
-               u32 hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
-
-               err = br_multicast_set_hash_max(br, hash_max);
-               if (err)
-                       return err;
-       }
+       if (data[IFLA_BR_MCAST_HASH_MAX])
+               br->hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
 
        if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
                u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
@@ -1296,6 +1291,15 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        }
 #endif
 
+       if (data[IFLA_BR_MULTI_BOOLOPT]) {
+               struct br_boolopt_multi *bm;
+
+               bm = nla_data(data[IFLA_BR_MULTI_BOOLOPT]);
+               err = br_boolopt_multi_toggle(br, bm, extack);
+               if (err)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -1374,6 +1378,7 @@ static size_t br_get_size(const struct net_device *brdev)
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IP6TABLES */
               nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_ARPTABLES */
 #endif
+              nla_total_size(sizeof(struct br_boolopt_multi)) + /* IFLA_BR_MULTI_BOOLOPT */
               0;
 }
 
@@ -1387,6 +1392,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        u32 stp_enabled = br->stp_enabled;
        u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
        u8 vlan_enabled = br_vlan_enabled(br->dev);
+       struct br_boolopt_multi bm;
        u64 clockval;
 
        clockval = br_timer_value(&br->hello_timer);
@@ -1403,6 +1409,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
        if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
                return -EMSGSIZE;
 
+       br_boolopt_multi_get(br, &bm);
        if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
            nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
            nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time) ||
@@ -1420,7 +1427,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
            nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
            nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
                       br->topology_change_detected) ||
-           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr))
+           nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) ||
+           nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm))
                return -EMSGSIZE;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
@@ -1442,8 +1450,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
                       br_opt_get(br, BROPT_MULTICAST_QUERIER)) ||
            nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
                       br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
-           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY,
-                       br->hash_elasticity) ||
+           nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
            nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
            nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
                        br->multicast_last_member_count) ||
index 04c19a3..5719b4d 100644 (file)
@@ -31,6 +31,8 @@
 #define BR_PORT_BITS   10
 #define BR_MAX_PORTS   (1<<BR_PORT_BITS)
 
+#define BR_MULTICAST_DEFAULT_HASH_MAX 4096
+
 #define BR_VERSION     "2.3"
 
 /* Control of forwarding link local multicast */
@@ -213,23 +215,14 @@ struct net_bridge_port_group {
 };
 
 struct net_bridge_mdb_entry {
-       struct hlist_node               hlist[2];
+       struct rhash_head               rhnode;
        struct net_bridge               *br;
        struct net_bridge_port_group __rcu *ports;
        struct rcu_head                 rcu;
        struct timer_list               timer;
        struct br_ip                    addr;
        bool                            host_joined;
-};
-
-struct net_bridge_mdb_htable {
-       struct hlist_head               *mhash;
-       struct rcu_head                 rcu;
-       struct net_bridge_mdb_htable    *old;
-       u32                             size;
-       u32                             max;
-       u32                             secret;
-       u32                             ver;
+       struct hlist_node               mdb_node;
 };
 
 struct net_bridge_port {
@@ -328,6 +321,7 @@ enum net_bridge_opts {
        BROPT_NEIGH_SUPPRESS_ENABLED,
        BROPT_MTU_SET_BY_USER,
        BROPT_VLAN_STATS_PER_PORT,
+       BROPT_NO_LL_LEARN,
 };
 
 struct net_bridge {
@@ -380,7 +374,6 @@ struct net_bridge {
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 
-       u32                             hash_elasticity;
        u32                             hash_max;
 
        u32                             multicast_last_member_count;
@@ -399,7 +392,9 @@ struct net_bridge {
        unsigned long                   multicast_query_response_interval;
        unsigned long                   multicast_startup_query_interval;
 
-       struct net_bridge_mdb_htable __rcu *mdb;
+       struct rhashtable               mdb_hash_tbl;
+
+       struct hlist_head               mdb_list;
        struct hlist_head               router_list;
 
        struct timer_list               multicast_router_timer;
@@ -507,6 +502,14 @@ static inline int br_opt_get(const struct net_bridge *br,
        return test_bit(opt, &br->options);
 }
 
+int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
+                     struct netlink_ext_ack *extack);
+int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt);
+int br_boolopt_multi_toggle(struct net_bridge *br,
+                           struct br_boolopt_multi *bm,
+                           struct netlink_ext_ack *extack);
+void br_boolopt_multi_get(const struct net_bridge *br,
+                         struct br_boolopt_multi *bm);
 void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on);
 
 /* br_device.c */
@@ -650,7 +653,6 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd,
 
 /* br_multicast.c */
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-extern unsigned int br_mdb_rehash_seq;
 int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
                     struct sk_buff *skb, u16 vid);
 struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
@@ -675,17 +677,15 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val);
 int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val);
 #endif
 struct net_bridge_mdb_entry *
-br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst);
+br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
 struct net_bridge_mdb_entry *
-br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port,
-                      struct br_ip *group);
-void br_multicast_free_pg(struct rcu_head *head);
+br_multicast_new_group(struct net_bridge *br, struct br_ip *group);
 struct net_bridge_port_group *
 br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
                            struct net_bridge_port_group __rcu *next,
                            unsigned char flags, const unsigned char *src);
-void br_mdb_init(void);
-void br_mdb_uninit(void);
+int br_mdb_hash_init(struct net_bridge *br);
+void br_mdb_hash_fini(struct net_bridge *br);
 void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
                   struct br_ip *group, int type, u8 flags);
 void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
@@ -697,6 +697,8 @@ void br_multicast_uninit_stats(struct net_bridge *br);
 void br_multicast_get_stats(const struct net_bridge *br,
                            const struct net_bridge_port *p,
                            struct br_mcast_stats *dest);
+void br_mdb_init(void);
+void br_mdb_uninit(void);
 
 #define mlock_dereference(X, br) \
        rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
@@ -822,6 +824,15 @@ static inline void br_mdb_uninit(void)
 {
 }
 
+static inline int br_mdb_hash_init(struct net_bridge *br)
+{
+       return 0;
+}
+
+static inline void br_mdb_hash_fini(struct net_bridge *br)
+{
+}
+
 static inline void br_multicast_count(struct net_bridge *br,
                                      const struct net_bridge_port *p,
                                      const struct sk_buff *skb,
@@ -912,7 +923,7 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid)
        int err = 0;
 
        if (skb_vlan_tag_present(skb)) {
-               *vid = skb_vlan_tag_get(skb) & VLAN_VID_MASK;
+               *vid = skb_vlan_tag_get_id(skb);
        } else {
                *vid = 0;
                err = -EINVAL;
index 60182be..b05b94e 100644 (file)
@@ -328,6 +328,27 @@ static ssize_t flush_store(struct device *d,
 }
 static DEVICE_ATTR_WO(flush);
 
+static ssize_t no_linklocal_learn_show(struct device *d,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%d\n", br_boolopt_get(br, BR_BOOLOPT_NO_LL_LEARN));
+}
+
+static int set_no_linklocal_learn(struct net_bridge *br, unsigned long val)
+{
+       return br_boolopt_toggle(br, BR_BOOLOPT_NO_LL_LEARN, !!val, NULL);
+}
+
+static ssize_t no_linklocal_learn_store(struct device *d,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, set_no_linklocal_learn);
+}
+static DEVICE_ATTR_RW(no_linklocal_learn);
+
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 static ssize_t multicast_router_show(struct device *d,
                                     struct device_attribute *attr, char *buf)
@@ -403,13 +424,13 @@ static DEVICE_ATTR_RW(multicast_querier);
 static ssize_t hash_elasticity_show(struct device *d,
                                    struct device_attribute *attr, char *buf)
 {
-       struct net_bridge *br = to_bridge(d);
-       return sprintf(buf, "%u\n", br->hash_elasticity);
+       return sprintf(buf, "%u\n", RHT_ELASTICITY);
 }
 
 static int set_elasticity(struct net_bridge *br, unsigned long val)
 {
-       br->hash_elasticity = val;
+       br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
+               RHT_ELASTICITY);
        return 0;
 }
 
@@ -428,10 +449,16 @@ static ssize_t hash_max_show(struct device *d, struct device_attribute *attr,
        return sprintf(buf, "%u\n", br->hash_max);
 }
 
+static int set_hash_max(struct net_bridge *br, unsigned long val)
+{
+       br->hash_max = val;
+       return 0;
+}
+
 static ssize_t hash_max_store(struct device *d, struct device_attribute *attr,
                              const char *buf, size_t len)
 {
-       return store_bridge_parm(d, buf, len, br_multicast_set_hash_max);
+       return store_bridge_parm(d, buf, len, set_hash_max);
 }
 static DEVICE_ATTR_RW(hash_max);
 
@@ -841,6 +868,7 @@ static struct attribute *bridge_attrs[] = {
        &dev_attr_gc_timer.attr,
        &dev_attr_group_addr.attr,
        &dev_attr_flush.attr,
+       &dev_attr_no_linklocal_learn.attr,
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        &dev_attr_multicast_router.attr,
        &dev_attr_multicast_snooping.attr,
index 7c87a2f..88715ed 100644 (file)
@@ -320,9 +320,6 @@ static ssize_t brport_store(struct kobject *kobj,
        if (!rtnl_trylock())
                return restart_syscall();
 
-       if (!p->dev || !p->br)
-               goto out_unlock;
-
        if (brport_attr->store_raw) {
                char *buf_copy;
 
index e84be08..48f50d7 100644 (file)
@@ -421,7 +421,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
        }
 
        if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
-               skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(skb);
 
        if (p && (p->flags & BR_VLAN_TUNNEL) &&
            br_handle_egress_vlan_tunnel(skb, v)) {
@@ -494,8 +494,8 @@ static bool __allowed_ingress(const struct net_bridge *br,
                        __vlan_hwaccel_put_tag(skb, br->vlan_proto, pvid);
                else
                        /* Priority-tagged Frame.
-                        * At this point, We know that skb->vlan_tci had
-                        * VLAN_TAG_PRESENT bit and its VID field was 0x000.
+                        * At this point, we know that skb->vlan_tci VID
+                        * field was 0.
                         * We update only VID field and preserve PCP field.
                         */
                        skb->vlan_tci |= pvid;
@@ -1217,9 +1217,13 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
 int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
 {
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p;
 
        ASSERT_RTNL();
-       if (netif_is_bridge_master(dev))
+       p = br_port_get_check_rtnl(dev);
+       if (p)
+               vg = nbp_vlan_group(p);
+       else if (netif_is_bridge_master(dev))
                vg = br_vlan_group(netdev_priv(dev));
        else
                return -EINVAL;
index 57f3a6f..4bf62b1 100644 (file)
@@ -728,49 +728,6 @@ fault:
        return -EFAULT;
 }
 
-__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
-{
-       __sum16 sum;
-
-       sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
-       if (likely(!sum)) {
-               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
-       }
-       if (!skb_shared(skb))
-               skb->csum_valid = !sum;
-       return sum;
-}
-EXPORT_SYMBOL(__skb_checksum_complete_head);
-
-__sum16 __skb_checksum_complete(struct sk_buff *skb)
-{
-       __wsum csum;
-       __sum16 sum;
-
-       csum = skb_checksum(skb, 0, skb->len, 0);
-
-       /* skb->csum holds pseudo checksum */
-       sum = csum_fold(csum_add(skb->csum, csum));
-       if (likely(!sum)) {
-               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
-       }
-
-       if (!skb_shared(skb)) {
-               /* Save full packet checksum */
-               skb->csum = csum;
-               skb->ip_summed = CHECKSUM_COMPLETE;
-               skb->csum_complete_sw = 1;
-               skb->csum_valid = !sum;
-       }
-
-       return sum;
-}
-EXPORT_SYMBOL(__skb_checksum_complete);
-
 /**
  *     skb_copy_and_csum_datagram_msg - Copy and checksum skb to user iovec.
  *     @skb: skbuff
@@ -810,7 +767,7 @@ int skb_copy_and_csum_datagram_msg(struct sk_buff *skb,
 
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(NULL);
+                       netdev_rx_csum_fault(NULL, skb);
        }
        return 0;
 fault:
index 722d50d..7542848 100644 (file)
@@ -162,6 +162,9 @@ static struct list_head offload_base __read_mostly;
 static int netif_rx_internal(struct sk_buff *skb);
 static int call_netdevice_notifiers_info(unsigned long val,
                                         struct netdev_notifier_info *info);
+static int call_netdevice_notifiers_extack(unsigned long val,
+                                          struct net_device *dev,
+                                          struct netlink_ext_ack *extack);
 static struct napi_struct *napi_by_id(unsigned int napi_id);
 
 /*
@@ -1361,7 +1364,7 @@ void netdev_notify_peers(struct net_device *dev)
 }
 EXPORT_SYMBOL(netdev_notify_peers);
 
-static int __dev_open(struct net_device *dev)
+static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
 {
        const struct net_device_ops *ops = dev->netdev_ops;
        int ret;
@@ -1377,7 +1380,7 @@ static int __dev_open(struct net_device *dev)
         */
        netpoll_poll_disable(dev);
 
-       ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev);
+       ret = call_netdevice_notifiers_extack(NETDEV_PRE_UP, dev, extack);
        ret = notifier_to_errno(ret);
        if (ret)
                return ret;
@@ -1406,7 +1409,8 @@ static int __dev_open(struct net_device *dev)
 
 /**
  *     dev_open        - prepare an interface for use.
- *     @dev:   device to open
+ *     @dev: device to open
+ *     @extack: netlink extended ack
  *
  *     Takes a device from down to up state. The device's private open
  *     function is invoked and then the multicast lists are loaded. Finally
@@ -1416,14 +1420,14 @@ static int __dev_open(struct net_device *dev)
  *     Calling this function on an active interface is a nop. On a failure
  *     a negative errno code is returned.
  */
-int dev_open(struct net_device *dev)
+int dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
 {
        int ret;
 
        if (dev->flags & IFF_UP)
                return 0;
 
-       ret = __dev_open(dev);
+       ret = __dev_open(dev, extack);
        if (ret < 0)
                return ret;
 
@@ -1733,6 +1737,18 @@ static int call_netdevice_notifiers_info(unsigned long val,
        return raw_notifier_call_chain(&netdev_chain, val, info);
 }
 
+static int call_netdevice_notifiers_extack(unsigned long val,
+                                          struct net_device *dev,
+                                          struct netlink_ext_ack *extack)
+{
+       struct netdev_notifier_info info = {
+               .dev = dev,
+               .extack = extack,
+       };
+
+       return call_netdevice_notifiers_info(val, &info);
+}
+
 /**
  *     call_netdevice_notifiers - call all network notifier blocks
  *      @val: value passed unmodified to notifier function
@@ -1744,11 +1760,7 @@ static int call_netdevice_notifiers_info(unsigned long val,
 
 int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
 {
-       struct netdev_notifier_info info = {
-               .dev = dev,
-       };
-
-       return call_netdevice_notifiers_info(val, &info);
+       return call_netdevice_notifiers_extack(val, dev, NULL);
 }
 EXPORT_SYMBOL(call_netdevice_notifiers);
 
@@ -3096,10 +3108,17 @@ EXPORT_SYMBOL(__skb_gso_segment);
 
 /* Take action when hardware reception checksum errors are detected. */
 #ifdef CONFIG_BUG
-void netdev_rx_csum_fault(struct net_device *dev)
+void netdev_rx_csum_fault(struct net_device *dev, struct sk_buff *skb)
 {
        if (net_ratelimit()) {
                pr_err("%s: hw csum failure\n", dev ? dev->name : "<unknown>");
+               if (dev)
+                       pr_err("dev features: %pNF\n", &dev->features);
+               pr_err("skb len=%u data_len=%u pkt_type=%u gso_size=%u gso_type=%u nr_frags=%u ip_summed=%u csum=%x csum_complete_sw=%d csum_valid=%d csum_level=%u\n",
+                      skb->len, skb->data_len, skb->pkt_type,
+                      skb_shinfo(skb)->gso_size, skb_shinfo(skb)->gso_type,
+                      skb_shinfo(skb)->nr_frags, skb->ip_summed, skb->csum,
+                      skb->csum_complete_sw, skb->csum_valid, skb->csum_level);
                dump_stack();
        }
 }
@@ -4525,9 +4544,14 @@ static int netif_rx_internal(struct sk_buff *skb)
 
 int netif_rx(struct sk_buff *skb)
 {
+       int ret;
+
        trace_netif_rx_entry(skb);
 
-       return netif_rx_internal(skb);
+       ret = netif_rx_internal(skb);
+       trace_netif_rx_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(netif_rx);
 
@@ -4542,6 +4566,7 @@ int netif_rx_ni(struct sk_buff *skb)
        if (local_softirq_pending())
                do_softirq();
        preempt_enable();
+       trace_netif_rx_ni_exit(err);
 
        return err;
 }
@@ -4894,7 +4919,7 @@ skip_classify:
                 * and set skb->priority like in vlan_do_receive()
                 * For the time being, just ignore Priority Code Point
                 */
-               skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(skb);
        }
 
        type = skb->protocol;
@@ -5227,9 +5252,14 @@ static void netif_receive_skb_list_internal(struct list_head *head)
  */
 int netif_receive_skb(struct sk_buff *skb)
 {
+       int ret;
+
        trace_netif_receive_skb_entry(skb);
 
-       return netif_receive_skb_internal(skb);
+       ret = netif_receive_skb_internal(skb);
+       trace_netif_receive_skb_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(netif_receive_skb);
 
@@ -5249,9 +5279,12 @@ void netif_receive_skb_list(struct list_head *head)
 
        if (list_empty(head))
                return;
-       list_for_each_entry(skb, head, list)
-               trace_netif_receive_skb_list_entry(skb);
+       if (trace_netif_receive_skb_list_entry_enabled()) {
+               list_for_each_entry(skb, head, list)
+                       trace_netif_receive_skb_list_entry(skb);
+       }
        netif_receive_skb_list_internal(head);
+       trace_netif_receive_skb_list_exit(0);
 }
 EXPORT_SYMBOL(netif_receive_skb_list);
 
@@ -5362,11 +5395,13 @@ static void __napi_gro_flush_chain(struct napi_struct *napi, u32 index,
  */
 void napi_gro_flush(struct napi_struct *napi, bool flush_old)
 {
-       u32 i;
+       unsigned long bitmask = napi->gro_bitmask;
+       unsigned int i, base = ~0U;
 
-       for (i = 0; i < GRO_HASH_BUCKETS; i++) {
-               if (test_bit(i, &napi->gro_bitmask))
-                       __napi_gro_flush_chain(napi, i, flush_old);
+       while ((i = ffs(bitmask)) != 0) {
+               bitmask >>= i;
+               base += i;
+               __napi_gro_flush_chain(napi, base, flush_old);
        }
 }
 EXPORT_SYMBOL(napi_gro_flush);
@@ -5391,7 +5426,9 @@ static struct list_head *gro_list_prepare(struct napi_struct *napi,
                }
 
                diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev;
-               diffs |= p->vlan_tci ^ skb->vlan_tci;
+               diffs |= skb_vlan_tag_present(p) ^ skb_vlan_tag_present(skb);
+               if (skb_vlan_tag_present(p))
+                       diffs |= p->vlan_tci ^ skb->vlan_tci;
                diffs |= skb_metadata_dst_cmp(p, skb);
                diffs |= skb_metadata_differs(p, skb);
                if (maclen == ETH_HLEN)
@@ -5639,12 +5676,17 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
 
 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
+       gro_result_t ret;
+
        skb_mark_napi_id(skb, napi);
        trace_napi_gro_receive_entry(skb);
 
        skb_gro_reset_offset(skb);
 
-       return napi_skb_finish(dev_gro_receive(napi, skb), skb);
+       ret = napi_skb_finish(dev_gro_receive(napi, skb), skb);
+       trace_napi_gro_receive_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(napi_gro_receive);
 
@@ -5657,7 +5699,7 @@ static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
        __skb_pull(skb, skb_headlen(skb));
        /* restore the reserve we had after netdev_alloc_skb_ip_align() */
        skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN - skb_headroom(skb));
-       skb->vlan_tci = 0;
+       __vlan_hwaccel_clear_tag(skb);
        skb->dev = napi->dev;
        skb->skb_iif = 0;
 
@@ -5762,6 +5804,7 @@ static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
 
 gro_result_t napi_gro_frags(struct napi_struct *napi)
 {
+       gro_result_t ret;
        struct sk_buff *skb = napi_frags_skb(napi);
 
        if (!skb)
@@ -5769,7 +5812,10 @@ gro_result_t napi_gro_frags(struct napi_struct *napi)
 
        trace_napi_gro_frags_entry(skb);
 
-       return napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
+       ret = napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
+       trace_napi_gro_frags_exit(ret);
+
+       return ret;
 }
 EXPORT_SYMBOL(napi_gro_frags);
 
@@ -5785,10 +5831,11 @@ __sum16 __skb_gro_checksum_complete(struct sk_buff *skb)
 
        /* NAPI_GRO_CB(skb)->csum holds pseudo checksum */
        sum = csum_fold(csum_add(NAPI_GRO_CB(skb)->csum, wsum));
+       /* See comments in __skb_checksum_complete(). */
        if (likely(!sum)) {
                if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
                    !skb->csum_complete_sw)
-                       netdev_rx_csum_fault(skb->dev);
+                       netdev_rx_csum_fault(skb->dev, skb);
        }
 
        NAPI_GRO_CB(skb)->csum = wsum;
@@ -7467,7 +7514,8 @@ unsigned int dev_get_flags(const struct net_device *dev)
 }
 EXPORT_SYMBOL(dev_get_flags);
 
-int __dev_change_flags(struct net_device *dev, unsigned int flags)
+int __dev_change_flags(struct net_device *dev, unsigned int flags,
+                      struct netlink_ext_ack *extack)
 {
        unsigned int old_flags = dev->flags;
        int ret;
@@ -7504,7 +7552,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
                if (old_flags & IFF_UP)
                        __dev_close(dev);
                else
-                       ret = __dev_open(dev);
+                       ret = __dev_open(dev, extack);
        }
 
        if ((flags ^ dev->gflags) & IFF_PROMISC) {
@@ -7564,16 +7612,18 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
  *     dev_change_flags - change device settings
  *     @dev: device
  *     @flags: device state flags
+ *     @extack: netlink extended ack
  *
  *     Change settings on device based state flags. The flags are
  *     in the userspace exported format.
  */
-int dev_change_flags(struct net_device *dev, unsigned int flags)
+int dev_change_flags(struct net_device *dev, unsigned int flags,
+                    struct netlink_ext_ack *extack)
 {
        int ret;
        unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;
 
-       ret = __dev_change_flags(dev, flags);
+       ret = __dev_change_flags(dev, flags, extack);
        if (ret < 0)
                return ret;
 
index d884d8f..81a8cd4 100644 (file)
@@ -278,6 +278,103 @@ int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
 EXPORT_SYMBOL(__hw_addr_sync_dev);
 
 /**
+ *  __hw_addr_ref_sync_dev - Synchronize device's multicast address list taking
+ *  into account references
+ *  @list: address list to synchronize
+ *  @dev:  device to sync
+ *  @sync: function to call if address or reference on it should be added
+ *  @unsync: function to call if address or some reference on it should removed
+ *
+ *  This function is intended to be called from the ndo_set_rx_mode
+ *  function of devices that require explicit address or references on it
+ *  add/remove notifications. The unsync function may be NULL in which case
+ *  the addresses or references on it requiring removal will simply be
+ *  removed without any notification to the device. That is responsibility of
+ *  the driver to identify and distribute address or references on it between
+ *  internal address tables.
+ **/
+int __hw_addr_ref_sync_dev(struct netdev_hw_addr_list *list,
+                          struct net_device *dev,
+                          int (*sync)(struct net_device *,
+                                      const unsigned char *, int),
+                          int (*unsync)(struct net_device *,
+                                        const unsigned char *, int))
+{
+       struct netdev_hw_addr *ha, *tmp;
+       int err, ref_cnt;
+
+       /* first go through and flush out any unsynced/stale entries */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               /* sync if address is not used */
+               if ((ha->sync_cnt << 1) <= ha->refcount)
+                       continue;
+
+               /* if fails defer unsyncing address */
+               ref_cnt = ha->refcount - ha->sync_cnt;
+               if (unsync && unsync(dev, ha->addr, ref_cnt))
+                       continue;
+
+               ha->refcount = (ref_cnt << 1) + 1;
+               ha->sync_cnt = ref_cnt;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+
+       /* go through and sync updated/new entries to the list */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               /* sync if address added or reused */
+               if ((ha->sync_cnt << 1) >= ha->refcount)
+                       continue;
+
+               ref_cnt = ha->refcount - ha->sync_cnt;
+               err = sync(dev, ha->addr, ref_cnt);
+               if (err)
+                       return err;
+
+               ha->refcount = ref_cnt << 1;
+               ha->sync_cnt = ref_cnt;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(__hw_addr_ref_sync_dev);
+
+/**
+ *  __hw_addr_ref_unsync_dev - Remove synchronized addresses and references on
+ *  it from device
+ *  @list: address list to remove synchronized addresses (references on it) from
+ *  @dev:  device to sync
+ *  @unsync: function to call if address and references on it should be removed
+ *
+ *  Remove all addresses that were added to the device by
+ *  __hw_addr_ref_sync_dev(). This function is intended to be called from the
+ *  ndo_stop or ndo_open functions on devices that require explicit address (or
+ *  references on it) add/remove notifications. If the unsync function pointer
+ *  is NULL then this function can be used to just reset the sync_cnt for the
+ *  addresses in the list.
+ **/
+void __hw_addr_ref_unsync_dev(struct netdev_hw_addr_list *list,
+                             struct net_device *dev,
+                             int (*unsync)(struct net_device *,
+                                           const unsigned char *, int))
+{
+       struct netdev_hw_addr *ha, *tmp;
+
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (!ha->sync_cnt)
+                       continue;
+
+               /* if fails defer unsyncing address */
+               if (unsync && unsync(dev, ha->addr, ha->sync_cnt))
+                       continue;
+
+               ha->refcount -= ha->sync_cnt - 1;
+               ha->sync_cnt = 0;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+}
+EXPORT_SYMBOL(__hw_addr_ref_unsync_dev);
+
+/**
  *  __hw_addr_unsync_dev - Remove synchronized addresses from device
  *  @list: address list to remove synchronized addresses from
  *  @dev:  device to sync
index 90e8aa3..da273ec 100644 (file)
@@ -234,7 +234,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
 
        switch (cmd) {
        case SIOCSIFFLAGS:      /* Set interface flags */
-               return dev_change_flags(dev, ifr->ifr_flags);
+               return dev_change_flags(dev, ifr->ifr_flags, NULL);
 
        case SIOCSIFMETRIC:     /* Set the metric on the interface
                                   (currently unused) */
index 3a4b29a..abb0da9 100644 (file)
@@ -2692,6 +2692,11 @@ static const struct devlink_param devlink_param_generic[] = {
                .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
                .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
        },
+       {
+               .id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
+               .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
+               .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
+       },
 };
 
 static int devlink_param_generic_verify(const struct devlink_param *param)
index 8d2c629..8659b40 100644 (file)
@@ -296,22 +296,18 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                break;
 
        case SKF_AD_VLAN_TAG:
-       case SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
                /* dst_reg = *(u16 *) (src_reg + offsetof(vlan_tci)) */
                *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
                                      offsetof(struct sk_buff, vlan_tci));
-               if (skb_field == SKF_AD_VLAN_TAG) {
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg,
-                                               ~VLAN_TAG_PRESENT);
-               } else {
-                       /* dst_reg >>= 12 */
-                       *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 12);
-                       /* dst_reg &= 1 */
+               break;
+       case SKF_AD_VLAN_TAG_PRESENT:
+               *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_VLAN_PRESENT_OFFSET());
+               if (PKT_VLAN_PRESENT_BIT)
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, PKT_VLAN_PRESENT_BIT);
+               if (PKT_VLAN_PRESENT_BIT < 7)
                        *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, 1);
-               }
                break;
        }
 
@@ -467,7 +463,8 @@ static bool convert_bpf_ld_abs(struct sock_filter *fp, struct bpf_insn **insnp)
                bool ldx_off_ok = offset <= S16_MAX;
 
                *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_H);
-               *insn++ = BPF_ALU64_IMM(BPF_SUB, BPF_REG_TMP, offset);
+               if (offset)
+                       *insn++ = BPF_ALU64_IMM(BPF_SUB, BPF_REG_TMP, offset);
                *insn++ = BPF_JMP_IMM(BPF_JSLT, BPF_REG_TMP,
                                      size, 2 + endian + (!ldx_off_ok * 2));
                if (ldx_off_ok) {
@@ -2428,6 +2425,174 @@ static const struct bpf_func_proto bpf_msg_push_data_proto = {
        .arg4_type      = ARG_ANYTHING,
 };
 
+static void sk_msg_shift_left(struct sk_msg *msg, int i)
+{
+       int prev;
+
+       do {
+               prev = i;
+               sk_msg_iter_var_next(i);
+               msg->sg.data[prev] = msg->sg.data[i];
+       } while (i != msg->sg.end);
+
+       sk_msg_iter_prev(msg, end);
+}
+
+static void sk_msg_shift_right(struct sk_msg *msg, int i)
+{
+       struct scatterlist tmp, sge;
+
+       sk_msg_iter_next(msg, end);
+       sge = sk_msg_elem_cpy(msg, i);
+       sk_msg_iter_var_next(i);
+       tmp = sk_msg_elem_cpy(msg, i);
+
+       while (i != msg->sg.end) {
+               msg->sg.data[i] = sge;
+               sk_msg_iter_var_next(i);
+               sge = tmp;
+               tmp = sk_msg_elem_cpy(msg, i);
+       }
+}
+
+BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start,
+          u32, len, u64, flags)
+{
+       u32 i = 0, l, space, offset = 0;
+       u64 last = start + len;
+       int pop;
+
+       if (unlikely(flags))
+               return -EINVAL;
+
+       /* First find the starting scatterlist element */
+       i = msg->sg.start;
+       do {
+               l = sk_msg_elem(msg, i)->length;
+
+               if (start < offset + l)
+                       break;
+               offset += l;
+               sk_msg_iter_var_next(i);
+       } while (i != msg->sg.end);
+
+       /* Bounds checks: start and pop must be inside message */
+       if (start >= offset + l || last >= msg->sg.size)
+               return -EINVAL;
+
+       space = MAX_MSG_FRAGS - sk_msg_elem_used(msg);
+
+       pop = len;
+       /* --------------| offset
+        * -| start      |-------- len -------|
+        *
+        *  |----- a ----|-------- pop -------|----- b ----|
+        *  |______________________________________________| length
+        *
+        *
+        * a:   region at front of scatter element to save
+        * b:   region at back of scatter element to save when length > A + pop
+        * pop: region to pop from element, same as input 'pop' here will be
+        *      decremented below per iteration.
+        *
+        * Two top-level cases to handle when start != offset, first B is non
+        * zero and second B is zero corresponding to when a pop includes more
+        * than one element.
+        *
+        * Then if B is non-zero AND there is no space allocate space and
+        * compact A, B regions into page. If there is space shift ring to
+        * the rigth free'ing the next element in ring to place B, leaving
+        * A untouched except to reduce length.
+        */
+       if (start != offset) {
+               struct scatterlist *nsge, *sge = sk_msg_elem(msg, i);
+               int a = start;
+               int b = sge->length - pop - a;
+
+               sk_msg_iter_var_next(i);
+
+               if (pop < sge->length - a) {
+                       if (space) {
+                               sge->length = a;
+                               sk_msg_shift_right(msg, i);
+                               nsge = sk_msg_elem(msg, i);
+                               get_page(sg_page(sge));
+                               sg_set_page(nsge,
+                                           sg_page(sge),
+                                           b, sge->offset + pop + a);
+                       } else {
+                               struct page *page, *orig;
+                               u8 *to, *from;
+
+                               page = alloc_pages(__GFP_NOWARN |
+                                                  __GFP_COMP   | GFP_ATOMIC,
+                                                  get_order(a + b));
+                               if (unlikely(!page))
+                                       return -ENOMEM;
+
+                               sge->length = a;
+                               orig = sg_page(sge);
+                               from = sg_virt(sge);
+                               to = page_address(page);
+                               memcpy(to, from, a);
+                               memcpy(to + a, from + a + pop, b);
+                               sg_set_page(sge, page, a + b, 0);
+                               put_page(orig);
+                       }
+                       pop = 0;
+               } else if (pop >= sge->length - a) {
+                       sge->length = a;
+                       pop -= (sge->length - a);
+               }
+       }
+
+       /* From above the current layout _must_ be as follows,
+        *
+        * -| offset
+        * -| start
+        *
+        *  |---- pop ---|---------------- b ------------|
+        *  |____________________________________________| length
+        *
+        * Offset and start of the current msg elem are equal because in the
+        * previous case we handled offset != start and either consumed the
+        * entire element and advanced to the next element OR pop == 0.
+        *
+        * Two cases to handle here are first pop is less than the length
+        * leaving some remainder b above. Simply adjust the element's layout
+        * in this case. Or pop >= length of the element so that b = 0. In this
+        * case advance to next element decrementing pop.
+        */
+       while (pop) {
+               struct scatterlist *sge = sk_msg_elem(msg, i);
+
+               if (pop < sge->length) {
+                       sge->length -= pop;
+                       sge->offset += pop;
+                       pop = 0;
+               } else {
+                       pop -= sge->length;
+                       sk_msg_shift_left(msg, i);
+               }
+               sk_msg_iter_var_next(i);
+       }
+
+       sk_mem_uncharge(msg->sk, len - pop);
+       msg->sg.size -= (len - pop);
+       sk_msg_compute_data_pointers(msg);
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_msg_pop_data_proto = {
+       .func           = bpf_msg_pop_data,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_ANYTHING,
+};
+
 BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb)
 {
        return task_get_classid(skb);
@@ -3908,6 +4073,26 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
        .arg1_type      = ARG_PTR_TO_CTX,
 };
 
+BPF_CALL_5(bpf_sockopt_event_output, struct bpf_sock_ops_kern *, bpf_sock,
+          struct bpf_map *, map, u64, flags, void *, data, u64, size)
+{
+       if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
+               return -EINVAL;
+
+       return bpf_event_output(map, flags, data, size, NULL, 0, NULL);
+}
+
+static const struct bpf_func_proto bpf_sockopt_event_output_proto =  {
+       .func           = bpf_sockopt_event_output,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_MEM,
+       .arg5_type      = ARG_CONST_SIZE_OR_ZERO,
+};
+
 BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
           int, level, int, optname, char *, optval, int, optlen)
 {
@@ -4825,37 +5010,31 @@ static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
 
 #ifdef CONFIG_INET
 static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
-                             struct sk_buff *skb, u8 family, u8 proto)
+                             int dif, int sdif, u8 family, u8 proto)
 {
        bool refcounted = false;
        struct sock *sk = NULL;
-       int dif = 0;
-
-       if (skb->dev)
-               dif = skb->dev->ifindex;
 
        if (family == AF_INET) {
                __be32 src4 = tuple->ipv4.saddr;
                __be32 dst4 = tuple->ipv4.daddr;
-               int sdif = inet_sdif(skb);
 
                if (proto == IPPROTO_TCP)
-                       sk = __inet_lookup(net, &tcp_hashinfo, skb, 0,
+                       sk = __inet_lookup(net, &tcp_hashinfo, NULL, 0,
                                           src4, tuple->ipv4.sport,
                                           dst4, tuple->ipv4.dport,
                                           dif, sdif, &refcounted);
                else
                        sk = __udp4_lib_lookup(net, src4, tuple->ipv4.sport,
                                               dst4, tuple->ipv4.dport,
-                                              dif, sdif, &udp_table, skb);
+                                              dif, sdif, &udp_table, NULL);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
                struct in6_addr *src6 = (struct in6_addr *)&tuple->ipv6.saddr;
                struct in6_addr *dst6 = (struct in6_addr *)&tuple->ipv6.daddr;
-               int sdif = inet6_sdif(skb);
 
                if (proto == IPPROTO_TCP)
-                       sk = __inet6_lookup(net, &tcp_hashinfo, skb, 0,
+                       sk = __inet6_lookup(net, &tcp_hashinfo, NULL, 0,
                                            src6, tuple->ipv6.sport,
                                            dst6, ntohs(tuple->ipv6.dport),
                                            dif, sdif, &refcounted);
@@ -4864,7 +5043,7 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
                                                            src6, tuple->ipv6.sport,
                                                            dst6, tuple->ipv6.dport,
                                                            dif, sdif,
-                                                           &udp_table, skb);
+                                                           &udp_table, NULL);
 #endif
        }
 
@@ -4881,31 +5060,33 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
  * callers to satisfy BPF_CALL declarations.
  */
 static unsigned long
-bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
-             u8 proto, u64 netns_id, u64 flags)
+__bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
+               struct net *caller_net, u32 ifindex, u8 proto, u64 netns_id,
+               u64 flags)
 {
-       struct net *caller_net;
        struct sock *sk = NULL;
        u8 family = AF_UNSPEC;
        struct net *net;
+       int sdif;
 
        family = len == sizeof(tuple->ipv4) ? AF_INET : AF_INET6;
        if (unlikely(family == AF_UNSPEC || flags ||
                     !((s32)netns_id < 0 || netns_id <= S32_MAX)))
                goto out;
 
-       if (skb->dev)
-               caller_net = dev_net(skb->dev);
+       if (family == AF_INET)
+               sdif = inet_sdif(skb);
        else
-               caller_net = sock_net(skb->sk);
+               sdif = inet6_sdif(skb);
+
        if ((s32)netns_id < 0) {
                net = caller_net;
-               sk = sk_lookup(net, tuple, skb, family, proto);
+               sk = sk_lookup(net, tuple, ifindex, sdif, family, proto);
        } else {
                net = get_net_ns_by_id(caller_net, netns_id);
                if (unlikely(!net))
                        goto out;
-               sk = sk_lookup(net, tuple, skb, family, proto);
+               sk = sk_lookup(net, tuple, ifindex, sdif, family, proto);
                put_net(net);
        }
 
@@ -4915,6 +5096,25 @@ out:
        return (unsigned long) sk;
 }
 
+static unsigned long
+bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
+             u8 proto, u64 netns_id, u64 flags)
+{
+       struct net *caller_net;
+       int ifindex;
+
+       if (skb->dev) {
+               caller_net = dev_net(skb->dev);
+               ifindex = skb->dev->ifindex;
+       } else {
+               caller_net = sock_net(skb->sk);
+               ifindex = 0;
+       }
+
+       return __bpf_sk_lookup(skb, tuple, len, caller_net, ifindex,
+                             proto, netns_id, flags);
+}
+
 BPF_CALL_5(bpf_sk_lookup_tcp, struct sk_buff *, skb,
           struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
 {
@@ -4964,6 +5164,87 @@ static const struct bpf_func_proto bpf_sk_release_proto = {
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_SOCKET,
 };
+
+BPF_CALL_5(bpf_xdp_sk_lookup_udp, struct xdp_buff *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags)
+{
+       struct net *caller_net = dev_net(ctx->rxq->dev);
+       int ifindex = ctx->rxq->dev->ifindex;
+
+       return __bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex,
+                             IPPROTO_UDP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = {
+       .func           = bpf_xdp_sk_lookup_udp,
+       .gpl_only       = false,
+       .pkt_access     = true,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_xdp_sk_lookup_tcp, struct xdp_buff *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u32, netns_id, u64, flags)
+{
+       struct net *caller_net = dev_net(ctx->rxq->dev);
+       int ifindex = ctx->rxq->dev->ifindex;
+
+       return __bpf_sk_lookup(NULL, tuple, len, caller_net, ifindex,
+                             IPPROTO_TCP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_xdp_sk_lookup_tcp_proto = {
+       .func           = bpf_xdp_sk_lookup_tcp,
+       .gpl_only       = false,
+       .pkt_access     = true,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_sock_addr_sk_lookup_tcp, struct bpf_sock_addr_kern *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
+{
+       return __bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0,
+                              IPPROTO_TCP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_sk_lookup_tcp_proto = {
+       .func           = bpf_sock_addr_sk_lookup_tcp,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_5(bpf_sock_addr_sk_lookup_udp, struct bpf_sock_addr_kern *, ctx,
+          struct bpf_sock_tuple *, tuple, u32, len, u64, netns_id, u64, flags)
+{
+       return __bpf_sk_lookup(NULL, tuple, len, sock_net(ctx->sk), 0,
+                              IPPROTO_UDP, netns_id, flags);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = {
+       .func           = bpf_sock_addr_sk_lookup_udp,
+       .gpl_only       = false,
+       .ret_type       = RET_PTR_TO_SOCKET_OR_NULL,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+       .arg5_type      = ARG_ANYTHING,
+};
+
 #endif /* CONFIG_INET */
 
 bool bpf_helper_changes_pkt_data(void *func)
@@ -4986,6 +5267,7 @@ bool bpf_helper_changes_pkt_data(void *func)
            func == bpf_xdp_adjust_meta ||
            func == bpf_msg_pull_data ||
            func == bpf_msg_push_data ||
+           func == bpf_msg_pop_data ||
            func == bpf_xdp_adjust_tail ||
 #if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
            func == bpf_lwt_seg6_store_bytes ||
@@ -5070,6 +5352,14 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_cookie_sock_addr_proto;
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
+#ifdef CONFIG_INET
+       case BPF_FUNC_sk_lookup_tcp:
+               return &bpf_sock_addr_sk_lookup_tcp_proto;
+       case BPF_FUNC_sk_lookup_udp:
+               return &bpf_sock_addr_sk_lookup_udp_proto;
+       case BPF_FUNC_sk_release:
+               return &bpf_sk_release_proto;
+#endif /* CONFIG_INET */
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5214,6 +5504,14 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_xdp_adjust_tail_proto;
        case BPF_FUNC_fib_lookup:
                return &bpf_xdp_fib_lookup_proto;
+#ifdef CONFIG_INET
+       case BPF_FUNC_sk_lookup_udp:
+               return &bpf_xdp_sk_lookup_udp_proto;
+       case BPF_FUNC_sk_lookup_tcp:
+               return &bpf_xdp_sk_lookup_tcp_proto;
+       case BPF_FUNC_sk_release:
+               return &bpf_sk_release_proto;
+#endif
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5240,6 +5538,8 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_cookie_sock_ops_proto;
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
+       case BPF_FUNC_perf_event_output:
+               return &bpf_sockopt_event_output_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5264,6 +5564,8 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_msg_pull_data_proto;
        case BPF_FUNC_msg_push_data:
                return &bpf_msg_push_data_proto;
+       case BPF_FUNC_msg_pop_data:
+               return &bpf_msg_pop_data_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -5440,6 +5742,10 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
                if (size != sizeof(__u64))
                        return false;
                break;
+       case bpf_ctx_range(struct __sk_buff, tstamp):
+               if (size != sizeof(__u64))
+                       return false;
+               break;
        default:
                /* Only narrow read access allowed for now. */
                if (type == BPF_WRITE) {
@@ -5467,6 +5773,7 @@ static bool sk_filter_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, data_end):
        case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
                return false;
        }
 
@@ -5505,6 +5812,10 @@ static bool cg_skb_is_valid_access(int off, int size,
                case bpf_ctx_range(struct __sk_buff, priority):
                case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
                        break;
+               case bpf_ctx_range(struct __sk_buff, tstamp):
+                       if (!capable(CAP_SYS_ADMIN))
+                               return false;
+                       break;
                default:
                        return false;
                }
@@ -5532,6 +5843,7 @@ static bool lwt_is_valid_access(int off, int size,
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
        case bpf_ctx_range(struct __sk_buff, data_meta):
        case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
                return false;
        }
 
@@ -5741,6 +6053,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
                case bpf_ctx_range(struct __sk_buff, priority):
                case bpf_ctx_range(struct __sk_buff, tc_classid):
                case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
+               case bpf_ctx_range(struct __sk_buff, tstamp):
                        break;
                default:
                        return false;
@@ -5960,6 +6273,7 @@ static bool sk_skb_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range(struct __sk_buff, data_meta):
        case bpf_ctx_range_ptr(struct __sk_buff, flow_keys):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
                return false;
        }
 
@@ -6046,6 +6360,7 @@ static bool flow_dissector_is_valid_access(int off, int size,
        case bpf_ctx_range(struct __sk_buff, tc_classid):
        case bpf_ctx_range(struct __sk_buff, data_meta):
        case bpf_ctx_range_till(struct __sk_buff, family, local_port):
+       case bpf_ctx_range(struct __sk_buff, tstamp):
                return false;
        }
 
@@ -6140,19 +6455,19 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                break;
 
        case offsetof(struct __sk_buff, vlan_present):
-       case offsetof(struct __sk_buff, vlan_tci):
-               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
+               *target_size = 1;
+               *insn++ = BPF_LDX_MEM(BPF_B, si->dst_reg, si->src_reg,
+                                     PKT_VLAN_PRESENT_OFFSET());
+               if (PKT_VLAN_PRESENT_BIT)
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, PKT_VLAN_PRESENT_BIT);
+               if (PKT_VLAN_PRESENT_BIT < 7)
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1);
+               break;
 
+       case offsetof(struct __sk_buff, vlan_tci):
                *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
                                      bpf_target_off(struct sk_buff, vlan_tci, 2,
                                                     target_size));
-               if (si->off == offsetof(struct __sk_buff, vlan_tci)) {
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg,
-                                               ~VLAN_TAG_PRESENT);
-               } else {
-                       *insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, 12);
-                       *insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1);
-               }
                break;
 
        case offsetof(struct __sk_buff, cb[0]) ...
@@ -6355,6 +6670,22 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg,
                                      si->src_reg, off);
                break;
+
+       case offsetof(struct __sk_buff, tstamp):
+               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tstamp) != 8);
+
+               if (type == BPF_WRITE)
+                       *insn++ = BPF_STX_MEM(BPF_DW,
+                                             si->dst_reg, si->src_reg,
+                                             bpf_target_off(struct sk_buff,
+                                                            tstamp, 8,
+                                                            target_size));
+               else
+                       *insn++ = BPF_LDX_MEM(BPF_DW,
+                                             si->dst_reg, si->src_reg,
+                                             bpf_target_off(struct sk_buff,
+                                                            tstamp, 8,
+                                                            target_size));
        }
 
        return insn - insn_buf;
index 588f475..2e8d91e 100644 (file)
@@ -952,8 +952,7 @@ proto_again:
 
                        if (!vlan) {
                                key_vlan->vlan_id = skb_vlan_tag_get_id(skb);
-                               key_vlan->vlan_priority =
-                                       (skb_vlan_tag_get_prio(skb) >> VLAN_PRIO_SHIFT);
+                               key_vlan->vlan_priority = skb_vlan_tag_get_prio(skb);
                        } else {
                                key_vlan->vlan_id = ntohs(vlan->h_vlan_TCI) &
                                        VLAN_VID_MASK;
index 41954e4..c3b5871 100644 (file)
@@ -118,6 +118,34 @@ unsigned long neigh_rand_reach_time(unsigned long base)
 }
 EXPORT_SYMBOL(neigh_rand_reach_time);
 
+static void neigh_mark_dead(struct neighbour *n)
+{
+       n->dead = 1;
+       if (!list_empty(&n->gc_list)) {
+               list_del_init(&n->gc_list);
+               atomic_dec(&n->tbl->gc_entries);
+       }
+}
+
+static void neigh_change_state(struct neighbour *n, u8 new)
+{
+       bool on_gc_list = !list_empty(&n->gc_list);
+       bool new_is_perm = new & NUD_PERMANENT;
+
+       n->nud_state = new;
+
+       /* remove from the gc list if new state is permanent;
+        * add to the gc list if new state is not permanent
+        */
+       if (new_is_perm && on_gc_list) {
+               list_del_init(&n->gc_list);
+               atomic_dec(&n->tbl->gc_entries);
+       } else if (!new_is_perm && !on_gc_list) {
+               /* add entries to the tail; cleaning removes from the front */
+               list_add_tail(&n->gc_list, &n->tbl->gc_list);
+               atomic_inc(&n->tbl->gc_entries);
+       }
+}
 
 static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags,
                      struct neighbour __rcu **np, struct neigh_table *tbl)
@@ -132,7 +160,7 @@ static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags,
                neigh = rcu_dereference_protected(n->next,
                                                  lockdep_is_held(&tbl->lock));
                rcu_assign_pointer(*np, neigh);
-               n->dead = 1;
+               neigh_mark_dead(n);
                retval = true;
        }
        write_unlock(&n->lock);
@@ -166,32 +194,31 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
 
 static int neigh_forced_gc(struct neigh_table *tbl)
 {
+       int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2;
+       unsigned long tref = jiffies - 5 * HZ;
+       u8 flags = NTF_EXT_LEARNED;
+       struct neighbour *n, *tmp;
+       u8 state = NUD_PERMANENT;
        int shrunk = 0;
-       int i;
-       struct neigh_hash_table *nht;
 
        NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
 
        write_lock_bh(&tbl->lock);
-       nht = rcu_dereference_protected(tbl->nht,
-                                       lockdep_is_held(&tbl->lock));
-       for (i = 0; i < (1 << nht->hash_shift); i++) {
-               struct neighbour *n;
-               struct neighbour __rcu **np;
 
-               np = &nht->hash_buckets[i];
-               while ((n = rcu_dereference_protected(*np,
-                                       lockdep_is_held(&tbl->lock))) != NULL) {
-                       /* Neighbour record may be discarded if:
-                        * - nobody refers to it.
-                        * - it is not permanent
-                        */
-                       if (neigh_del(n, NUD_PERMANENT, NTF_EXT_LEARNED, np,
-                                     tbl)) {
-                               shrunk = 1;
-                               continue;
-                       }
-                       np = &n->next;
+       list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) {
+               if (refcount_read(&n->refcnt) == 1) {
+                       bool remove = false;
+
+                       write_lock(&n->lock);
+                       if (!(n->nud_state & state) && !(n->flags & flags) &&
+                           time_after(tref, n->updated))
+                               remove = true;
+                       write_unlock(&n->lock);
+
+                       if (remove && neigh_remove_one(n, tbl))
+                               shrunk++;
+                       if (shrunk >= max_clean)
+                               break;
                }
        }
 
@@ -260,8 +287,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
                                                lockdep_is_held(&tbl->lock)));
                        write_lock(&n->lock);
                        neigh_del_timer(n);
-                       n->dead = 1;
-
+                       neigh_mark_dead(n);
                        if (refcount_read(&n->refcnt) != 1) {
                                /* The most unpleasant situation.
                                   We must destroy neighbour entry,
@@ -321,13 +347,18 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
 }
 EXPORT_SYMBOL(neigh_ifdown);
 
-static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev)
+static struct neighbour *neigh_alloc(struct neigh_table *tbl,
+                                    struct net_device *dev,
+                                    bool permanent)
 {
        struct neighbour *n = NULL;
        unsigned long now = jiffies;
        int entries;
 
-       entries = atomic_inc_return(&tbl->entries) - 1;
+       if (permanent)
+               goto do_alloc;
+
+       entries = atomic_inc_return(&tbl->gc_entries) - 1;
        if (entries >= tbl->gc_thresh3 ||
            (entries >= tbl->gc_thresh2 &&
             time_after(now, tbl->last_flush + 5 * HZ))) {
@@ -340,6 +371,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device
                }
        }
 
+do_alloc:
        n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC);
        if (!n)
                goto out_entries;
@@ -358,11 +390,19 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device
        n->tbl            = tbl;
        refcount_set(&n->refcnt, 1);
        n->dead           = 1;
+
+       if (!permanent)
+               list_add_tail(&n->gc_list, &n->tbl->gc_list);
+       else
+               INIT_LIST_HEAD(&n->gc_list);
+
+       atomic_inc(&tbl->entries);
 out:
        return n;
 
 out_entries:
-       atomic_dec(&tbl->entries);
+       if (!permanent)
+               atomic_dec(&tbl->gc_entries);
        goto out;
 }
 
@@ -505,13 +545,15 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
 }
 EXPORT_SYMBOL(neigh_lookup_nodev);
 
-struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
-                                struct net_device *dev, bool want_ref)
+static struct neighbour *___neigh_create(struct neigh_table *tbl,
+                                        const void *pkey,
+                                        struct net_device *dev,
+                                        bool permanent, bool want_ref)
 {
+       struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, permanent);
        u32 hash_val;
        unsigned int key_len = tbl->key_len;
        int error;
-       struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev);
        struct neigh_hash_table *nht;
 
        if (!n) {
@@ -591,6 +633,12 @@ out_neigh_release:
        neigh_release(n);
        goto out;
 }
+
+struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
+                                struct net_device *dev, bool want_ref)
+{
+       return ___neigh_create(tbl, pkey, dev, false, want_ref);
+}
 EXPORT_SYMBOL(__neigh_create);
 
 static u32 pneigh_hash(const void *pkey, unsigned int key_len)
@@ -854,7 +902,7 @@ static void neigh_periodic_work(struct work_struct *work)
                            (state == NUD_FAILED ||
                             time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
                                *np = n->next;
-                               n->dead = 1;
+                               neigh_mark_dead(n);
                                write_unlock(&n->lock);
                                neigh_cleanup_and_release(n);
                                continue;
@@ -1137,8 +1185,9 @@ static void neigh_update_hhs(struct neighbour *neigh)
    Caller MUST hold reference count on the entry.
  */
 
-int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
-                u32 flags, u32 nlmsg_pid)
+static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
+                         u8 new, u32 flags, u32 nlmsg_pid,
+                         struct netlink_ext_ack *extack)
 {
        u8 old;
        int err;
@@ -1155,8 +1204,10 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
        if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
            (old & (NUD_NOARP | NUD_PERMANENT)))
                goto out;
-       if (neigh->dead)
+       if (neigh->dead) {
+               NL_SET_ERR_MSG(extack, "Neighbor entry is now dead");
                goto out;
+       }
 
        neigh_update_ext_learned(neigh, flags, &notify);
 
@@ -1164,7 +1215,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                neigh_del_timer(neigh);
                if (old & NUD_CONNECTED)
                        neigh_suspect(neigh);
-               neigh->nud_state = new;
+               neigh_change_state(neigh, new);
                err = 0;
                notify = old & NUD_VALID;
                if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
@@ -1193,8 +1244,10 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                   use it, otherwise discard the request.
                 */
                err = -EINVAL;
-               if (!(old & NUD_VALID))
+               if (!(old & NUD_VALID)) {
+                       NL_SET_ERR_MSG(extack, "No link layer address given");
                        goto out;
+               }
                lladdr = neigh->ha;
        }
 
@@ -1241,7 +1294,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
                                                ((new & NUD_REACHABLE) ?
                                                 neigh->parms->reachable_time :
                                                 0)));
-               neigh->nud_state = new;
+               neigh_change_state(neigh, new);
                notify = 1;
        }
 
@@ -1307,6 +1360,12 @@ out:
 
        return err;
 }
+
+int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
+                u32 flags, u32 nlmsg_pid)
+{
+       return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);
+}
 EXPORT_SYMBOL(neigh_update);
 
 /* Update the neigh to listen temporarily for probe responses, even if it is
@@ -1571,6 +1630,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
        unsigned long phsize;
 
        INIT_LIST_HEAD(&tbl->parms_list);
+       INIT_LIST_HEAD(&tbl->gc_list);
        list_add(&tbl->parms.list, &tbl->parms_list);
        write_pnet(&tbl->parms.net, &init_net);
        refcount_set(&tbl->parms.refcnt, 1);
@@ -1678,8 +1738,10 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
 
        dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST);
-       if (dst_attr == NULL)
+       if (!dst_attr) {
+               NL_SET_ERR_MSG(extack, "Network address not specified");
                goto out;
+       }
 
        ndm = nlmsg_data(nlh);
        if (ndm->ndm_ifindex) {
@@ -1694,8 +1756,10 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (tbl == NULL)
                return -EAFNOSUPPORT;
 
-       if (nla_len(dst_attr) < (int)tbl->key_len)
+       if (nla_len(dst_attr) < (int)tbl->key_len) {
+               NL_SET_ERR_MSG(extack, "Invalid network address");
                goto out;
+       }
 
        if (ndm->ndm_flags & NTF_PROXY) {
                err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
@@ -1711,10 +1775,9 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
        }
 
-       err = neigh_update(neigh, NULL, NUD_FAILED,
-                          NEIGH_UPDATE_F_OVERRIDE |
-                          NEIGH_UPDATE_F_ADMIN,
-                          NETLINK_CB(skb).portid);
+       err = __neigh_update(neigh, NULL, NUD_FAILED,
+                            NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN,
+                            NETLINK_CB(skb).portid, extack);
        write_lock_bh(&tbl->lock);
        neigh_release(neigh);
        neigh_remove_one(neigh, tbl);
@@ -1744,8 +1807,10 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
 
        err = -EINVAL;
-       if (tb[NDA_DST] == NULL)
+       if (!tb[NDA_DST]) {
+               NL_SET_ERR_MSG(extack, "Network address not specified");
                goto out;
+       }
 
        ndm = nlmsg_data(nlh);
        if (ndm->ndm_ifindex) {
@@ -1755,16 +1820,21 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                        goto out;
                }
 
-               if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len)
+               if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) {
+                       NL_SET_ERR_MSG(extack, "Invalid link address");
                        goto out;
+               }
        }
 
        tbl = neigh_find_table(ndm->ndm_family);
        if (tbl == NULL)
                return -EAFNOSUPPORT;
 
-       if (nla_len(tb[NDA_DST]) < (int)tbl->key_len)
+       if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) {
+               NL_SET_ERR_MSG(extack, "Invalid network address");
                goto out;
+       }
+
        dst = nla_data(tb[NDA_DST]);
        lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
 
@@ -1780,8 +1850,10 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                goto out;
        }
 
-       if (dev == NULL)
+       if (!dev) {
+               NL_SET_ERR_MSG(extack, "Device not specified");
                goto out;
+       }
 
        neigh = neigh_lookup(tbl, dst, dev);
        if (neigh == NULL) {
@@ -1790,7 +1862,9 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                        goto out;
                }
 
-               neigh = __neigh_lookup_errno(tbl, dst, dev);
+               neigh = ___neigh_create(tbl, dst, dev,
+                                       ndm->ndm_state & NUD_PERMANENT,
+                                       true);
                if (IS_ERR(neigh)) {
                        err = PTR_ERR(neigh);
                        goto out;
@@ -1817,8 +1891,8 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                neigh_event_send(neigh, NULL);
                err = 0;
        } else
-               err = neigh_update(neigh, lladdr, ndm->ndm_state, flags,
-                                  NETLINK_CB(skb).portid);
+               err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
+                                    NETLINK_CB(skb).portid, extack);
        neigh_release(neigh);
 
 out:
@@ -2631,7 +2705,7 @@ void __neigh_for_each_release(struct neigh_table *tbl,
                                rcu_assign_pointer(*np,
                                        rcu_dereference_protected(n->next,
                                                lockdep_is_held(&tbl->lock)));
-                               n->dead = 1;
+                               neigh_mark_dead(n);
                        } else
                                np = &n->next;
                        write_unlock(&n->lock);
index bd67c4d..ff9fd2b 100644 (file)
@@ -337,7 +337,7 @@ NETDEVICE_SHOW_RW(mtu, fmt_dec);
 
 static int change_flags(struct net_device *dev, unsigned long new_flags)
 {
-       return dev_change_flags(dev, (unsigned int)new_flags);
+       return dev_change_flags(dev, (unsigned int)new_flags, NULL);
 }
 
 static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
index fefe727..05b23b2 100644 (file)
@@ -669,6 +669,7 @@ static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = {
        [NETNSA_NSID]           = { .type = NLA_S32 },
        [NETNSA_PID]            = { .type = NLA_U32 },
        [NETNSA_FD]             = { .type = NLA_U32 },
+       [NETNSA_TARGET_NSID]    = { .type = NLA_S32 },
 };
 
 static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -735,23 +736,38 @@ static int rtnl_net_get_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct rtgenmsg))
               + nla_total_size(sizeof(s32)) /* NETNSA_NSID */
+              + nla_total_size(sizeof(s32)) /* NETNSA_CURRENT_NSID */
               ;
 }
 
-static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
-                        int cmd, struct net *net, int nsid)
+struct net_fill_args {
+       u32 portid;
+       u32 seq;
+       int flags;
+       int cmd;
+       int nsid;
+       bool add_ref;
+       int ref_nsid;
+};
+
+static int rtnl_net_fill(struct sk_buff *skb, struct net_fill_args *args)
 {
        struct nlmsghdr *nlh;
        struct rtgenmsg *rth;
 
-       nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags);
+       nlh = nlmsg_put(skb, args->portid, args->seq, args->cmd, sizeof(*rth),
+                       args->flags);
        if (!nlh)
                return -EMSGSIZE;
 
        rth = nlmsg_data(nlh);
        rth->rtgen_family = AF_UNSPEC;
 
-       if (nla_put_s32(skb, NETNSA_NSID, nsid))
+       if (nla_put_s32(skb, NETNSA_NSID, args->nsid))
+               goto nla_put_failure;
+
+       if (args->add_ref &&
+           nla_put_s32(skb, NETNSA_CURRENT_NSID, args->ref_nsid))
                goto nla_put_failure;
 
        nlmsg_end(skb, nlh);
@@ -767,10 +783,15 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[NETNSA_MAX + 1];
+       struct net_fill_args fillargs = {
+               .portid = NETLINK_CB(skb).portid,
+               .seq = nlh->nlmsg_seq,
+               .cmd = RTM_NEWNSID,
+       };
+       struct net *peer, *target = net;
        struct nlattr *nla;
        struct sk_buff *msg;
-       struct net *peer;
-       int err, id;
+       int err;
 
        err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
                          rtnl_net_policy, extack);
@@ -782,6 +803,11 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
        } else if (tb[NETNSA_FD]) {
                peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD]));
                nla = tb[NETNSA_FD];
+       } else if (tb[NETNSA_NSID]) {
+               peer = get_net_ns_by_id(net, nla_get_u32(tb[NETNSA_NSID]));
+               if (!peer)
+                       peer = ERR_PTR(-ENOENT);
+               nla = tb[NETNSA_NSID];
        } else {
                NL_SET_ERR_MSG(extack, "Peer netns reference is missing");
                return -EINVAL;
@@ -793,15 +819,29 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
                return PTR_ERR(peer);
        }
 
+       if (tb[NETNSA_TARGET_NSID]) {
+               int id = nla_get_s32(tb[NETNSA_TARGET_NSID]);
+
+               target = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, id);
+               if (IS_ERR(target)) {
+                       NL_SET_BAD_ATTR(extack, tb[NETNSA_TARGET_NSID]);
+                       NL_SET_ERR_MSG(extack,
+                                      "Target netns reference is invalid");
+                       err = PTR_ERR(target);
+                       goto out;
+               }
+               fillargs.add_ref = true;
+               fillargs.ref_nsid = peernet2id(net, peer);
+       }
+
        msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
        if (!msg) {
                err = -ENOMEM;
                goto out;
        }
 
-       id = peernet2id(net, peer);
-       err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
-                           RTM_NEWNSID, net, id);
+       fillargs.nsid = peernet2id(target, peer);
+       err = rtnl_net_fill(msg, &fillargs);
        if (err < 0)
                goto err_out;
 
@@ -811,14 +851,17 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh,
 err_out:
        nlmsg_free(msg);
 out:
+       if (fillargs.add_ref)
+               put_net(target);
        put_net(peer);
        return err;
 }
 
 struct rtnl_net_dump_cb {
-       struct net *net;
+       struct net *tgt_net;
+       struct net *ref_net;
        struct sk_buff *skb;
-       struct netlink_callback *cb;
+       struct net_fill_args fillargs;
        int idx;
        int s_idx;
 };
@@ -831,9 +874,10 @@ static int rtnl_net_dumpid_one(int id, void *peer, void *data)
        if (net_cb->idx < net_cb->s_idx)
                goto cont;
 
-       ret = rtnl_net_fill(net_cb->skb, NETLINK_CB(net_cb->cb->skb).portid,
-                           net_cb->cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                           RTM_NEWNSID, net_cb->net, id);
+       net_cb->fillargs.nsid = id;
+       if (net_cb->fillargs.add_ref)
+               net_cb->fillargs.ref_nsid = __peernet2id(net_cb->ref_net, peer);
+       ret = rtnl_net_fill(net_cb->skb, &net_cb->fillargs);
        if (ret < 0)
                return ret;
 
@@ -842,33 +886,96 @@ cont:
        return 0;
 }
 
+static int rtnl_valid_dump_net_req(const struct nlmsghdr *nlh, struct sock *sk,
+                                  struct rtnl_net_dump_cb *net_cb,
+                                  struct netlink_callback *cb)
+{
+       struct netlink_ext_ack *extack = cb->extack;
+       struct nlattr *tb[NETNSA_MAX + 1];
+       int err, i;
+
+       err = nlmsg_parse_strict(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
+                                rtnl_net_policy, extack);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i <= NETNSA_MAX; i++) {
+               if (!tb[i])
+                       continue;
+
+               if (i == NETNSA_TARGET_NSID) {
+                       struct net *net;
+
+                       net = rtnl_get_net_ns_capable(sk, nla_get_s32(tb[i]));
+                       if (IS_ERR(net)) {
+                               NL_SET_BAD_ATTR(extack, tb[i]);
+                               NL_SET_ERR_MSG(extack,
+                                              "Invalid target network namespace id");
+                               return PTR_ERR(net);
+                       }
+                       net_cb->fillargs.add_ref = true;
+                       net_cb->ref_net = net_cb->tgt_net;
+                       net_cb->tgt_net = net;
+               } else {
+                       NL_SET_BAD_ATTR(extack, tb[i]);
+                       NL_SET_ERR_MSG(extack,
+                                      "Unsupported attribute in dump request");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
 static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct net *net = sock_net(skb->sk);
        struct rtnl_net_dump_cb net_cb = {
-               .net = net,
+               .tgt_net = sock_net(skb->sk),
                .skb = skb,
-               .cb = cb,
+               .fillargs = {
+                       .portid = NETLINK_CB(cb->skb).portid,
+                       .seq = cb->nlh->nlmsg_seq,
+                       .flags = NLM_F_MULTI,
+                       .cmd = RTM_NEWNSID,
+               },
                .idx = 0,
                .s_idx = cb->args[0],
        };
+       int err = 0;
 
-       if (cb->strict_check &&
-           nlmsg_attrlen(cb->nlh, sizeof(struct rtgenmsg))) {
-                       NL_SET_ERR_MSG(cb->extack, "Unknown data in network namespace id dump request");
-                       return -EINVAL;
+       if (cb->strict_check) {
+               err = rtnl_valid_dump_net_req(cb->nlh, skb->sk, &net_cb, cb);
+               if (err < 0)
+                       goto end;
        }
 
-       spin_lock_bh(&net->nsid_lock);
-       idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
-       spin_unlock_bh(&net->nsid_lock);
+       spin_lock_bh(&net_cb.tgt_net->nsid_lock);
+       if (net_cb.fillargs.add_ref &&
+           !net_eq(net_cb.ref_net, net_cb.tgt_net) &&
+           !spin_trylock_bh(&net_cb.ref_net->nsid_lock)) {
+               spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
+               err = -EAGAIN;
+               goto end;
+       }
+       idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb);
+       if (net_cb.fillargs.add_ref &&
+           !net_eq(net_cb.ref_net, net_cb.tgt_net))
+               spin_unlock_bh(&net_cb.ref_net->nsid_lock);
+       spin_unlock_bh(&net_cb.tgt_net->nsid_lock);
 
        cb->args[0] = net_cb.idx;
-       return skb->len;
+end:
+       if (net_cb.fillargs.add_ref)
+               put_net(net_cb.tgt_net);
+       return err < 0 ? err : skb->len;
 }
 
 static void rtnl_net_notifyid(struct net *net, int cmd, int id)
 {
+       struct net_fill_args fillargs = {
+               .cmd = cmd,
+               .nsid = id,
+       };
        struct sk_buff *msg;
        int err = -ENOMEM;
 
@@ -876,7 +983,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id)
        if (!msg)
                goto out;
 
-       err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, id);
+       err = rtnl_net_fill(msg, &fillargs);
        if (err < 0)
                goto err_out;
 
index 2b9fdbc..36a2b63 100644 (file)
@@ -663,7 +663,7 @@ int netpoll_setup(struct netpoll *np)
 
                np_info(np, "device %s not up yet, forcing it\n", np->dev_name);
 
-               err = dev_open(ndev);
+               err = dev_open(ndev, NULL);
 
                if (err) {
                        np_err(np, "failed to open %s\n", ndev->name);
index 7819f78..c9c0407 100644 (file)
@@ -59,7 +59,7 @@
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
 
-#define RTNL_MAX_TYPE          49
+#define RTNL_MAX_TYPE          50
 #define RTNL_SLAVE_MAX_TYPE    36
 
 struct rtnl_link {
@@ -2489,7 +2489,8 @@ static int do_setlink(const struct sk_buff *skb,
        }
 
        if (ifm->ifi_flags || ifm->ifi_change) {
-               err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
+               err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
+                                      extack);
                if (err < 0)
                        goto errout;
        }
@@ -2870,7 +2871,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
 
        old_flags = dev->flags;
        if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
-               err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm));
+               err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
+                                        NULL);
                if (err < 0)
                        return err;
        }
@@ -2885,9 +2887,11 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
 }
 EXPORT_SYMBOL(rtnl_configure_link);
 
-struct net_device *rtnl_create_link(struct net *net,
-       const char *ifname, unsigned char name_assign_type,
-       const struct rtnl_link_ops *ops, struct nlattr *tb[])
+struct net_device *rtnl_create_link(struct net *net, const char *ifname,
+                                   unsigned char name_assign_type,
+                                   const struct rtnl_link_ops *ops,
+                                   struct nlattr *tb[],
+                                   struct netlink_ext_ack *extack)
 {
        struct net_device *dev;
        unsigned int num_tx_queues = 1;
@@ -2903,11 +2907,15 @@ struct net_device *rtnl_create_link(struct net *net,
        else if (ops->get_num_rx_queues)
                num_rx_queues = ops->get_num_rx_queues();
 
-       if (num_tx_queues < 1 || num_tx_queues > 4096)
+       if (num_tx_queues < 1 || num_tx_queues > 4096) {
+               NL_SET_ERR_MSG(extack, "Invalid number of transmit queues");
                return ERR_PTR(-EINVAL);
+       }
 
-       if (num_rx_queues < 1 || num_rx_queues > 4096)
+       if (num_rx_queues < 1 || num_rx_queues > 4096) {
+               NL_SET_ERR_MSG(extack, "Invalid number of receive queues");
                return ERR_PTR(-EINVAL);
+       }
 
        dev = alloc_netdev_mqs(ops->priv_size, ifname, name_assign_type,
                               ops->setup, num_tx_queues, num_rx_queues);
@@ -2965,20 +2973,24 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
        return 0;
 }
 
-static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
-                       struct netlink_ext_ack *extack)
+static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+                         struct nlattr **attr, struct netlink_ext_ack *extack)
 {
+       struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
+       unsigned char name_assign_type = NET_NAME_USER;
+       struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
+       const struct rtnl_link_ops *m_ops = NULL;
+       struct net_device *master_dev = NULL;
        struct net *net = sock_net(skb->sk);
        const struct rtnl_link_ops *ops;
-       const struct rtnl_link_ops *m_ops = NULL;
+       struct nlattr *tb[IFLA_MAX + 1];
+       struct net *dest_net, *link_net;
+       struct nlattr **slave_data;
+       char kind[MODULE_NAME_LEN];
        struct net_device *dev;
-       struct net_device *master_dev = NULL;
        struct ifinfomsg *ifm;
-       char kind[MODULE_NAME_LEN];
        char ifname[IFNAMSIZ];
-       struct nlattr *tb[IFLA_MAX+1];
-       struct nlattr *linkinfo[IFLA_INFO_MAX+1];
-       unsigned char name_assign_type = NET_NAME_USER;
+       struct nlattr **data;
        int err;
 
 #ifdef CONFIG_MODULES
@@ -3034,193 +3046,200 @@ replay:
                ops = NULL;
        }
 
-       if (1) {
-               struct nlattr *attr[RTNL_MAX_TYPE + 1];
-               struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
-               struct nlattr **data = NULL;
-               struct nlattr **slave_data = NULL;
-               struct net *dest_net, *link_net = NULL;
-
-               if (ops) {
-                       if (ops->maxtype > RTNL_MAX_TYPE)
-                               return -EINVAL;
+       data = NULL;
+       if (ops) {
+               if (ops->maxtype > RTNL_MAX_TYPE)
+                       return -EINVAL;
 
-                       if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
-                               err = nla_parse_nested(attr, ops->maxtype,
-                                                      linkinfo[IFLA_INFO_DATA],
-                                                      ops->policy, NULL);
-                               if (err < 0)
-                                       return err;
-                               data = attr;
-                       }
-                       if (ops->validate) {
-                               err = ops->validate(tb, data, extack);
-                               if (err < 0)
-                                       return err;
-                       }
+               if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
+                       err = nla_parse_nested(attr, ops->maxtype,
+                                              linkinfo[IFLA_INFO_DATA],
+                                              ops->policy, extack);
+                       if (err < 0)
+                               return err;
+                       data = attr;
+               }
+               if (ops->validate) {
+                       err = ops->validate(tb, data, extack);
+                       if (err < 0)
+                               return err;
                }
+       }
 
-               if (m_ops) {
-                       if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
-                               return -EINVAL;
+       slave_data = NULL;
+       if (m_ops) {
+               if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
+                       return -EINVAL;
 
-                       if (m_ops->slave_maxtype &&
-                           linkinfo[IFLA_INFO_SLAVE_DATA]) {
-                               err = nla_parse_nested(slave_attr,
-                                                      m_ops->slave_maxtype,
-                                                      linkinfo[IFLA_INFO_SLAVE_DATA],
-                                                      m_ops->slave_policy,
-                                                      NULL);
-                               if (err < 0)
-                                       return err;
-                               slave_data = slave_attr;
-                       }
+               if (m_ops->slave_maxtype &&
+                   linkinfo[IFLA_INFO_SLAVE_DATA]) {
+                       err = nla_parse_nested(slave_attr, m_ops->slave_maxtype,
+                                              linkinfo[IFLA_INFO_SLAVE_DATA],
+                                              m_ops->slave_policy, extack);
+                       if (err < 0)
+                               return err;
+                       slave_data = slave_attr;
                }
+       }
 
-               if (dev) {
-                       int status = 0;
-
-                       if (nlh->nlmsg_flags & NLM_F_EXCL)
-                               return -EEXIST;
-                       if (nlh->nlmsg_flags & NLM_F_REPLACE)
-                               return -EOPNOTSUPP;
+       if (dev) {
+               int status = 0;
 
-                       if (linkinfo[IFLA_INFO_DATA]) {
-                               if (!ops || ops != dev->rtnl_link_ops ||
-                                   !ops->changelink)
-                                       return -EOPNOTSUPP;
+               if (nlh->nlmsg_flags & NLM_F_EXCL)
+                       return -EEXIST;
+               if (nlh->nlmsg_flags & NLM_F_REPLACE)
+                       return -EOPNOTSUPP;
 
-                               err = ops->changelink(dev, tb, data, extack);
-                               if (err < 0)
-                                       return err;
-                               status |= DO_SETLINK_NOTIFY;
-                       }
+               if (linkinfo[IFLA_INFO_DATA]) {
+                       if (!ops || ops != dev->rtnl_link_ops ||
+                           !ops->changelink)
+                               return -EOPNOTSUPP;
 
-                       if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
-                               if (!m_ops || !m_ops->slave_changelink)
-                                       return -EOPNOTSUPP;
+                       err = ops->changelink(dev, tb, data, extack);
+                       if (err < 0)
+                               return err;
+                       status |= DO_SETLINK_NOTIFY;
+               }
 
-                               err = m_ops->slave_changelink(master_dev, dev,
-                                                             tb, slave_data,
-                                                             extack);
-                               if (err < 0)
-                                       return err;
-                               status |= DO_SETLINK_NOTIFY;
-                       }
+               if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
+                       if (!m_ops || !m_ops->slave_changelink)
+                               return -EOPNOTSUPP;
 
-                       return do_setlink(skb, dev, ifm, extack, tb, ifname,
-                                         status);
+                       err = m_ops->slave_changelink(master_dev, dev, tb,
+                                                     slave_data, extack);
+                       if (err < 0)
+                               return err;
+                       status |= DO_SETLINK_NOTIFY;
                }
 
-               if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
-                       if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
-                               return rtnl_group_changelink(skb, net,
+               return do_setlink(skb, dev, ifm, extack, tb, ifname, status);
+       }
+
+       if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
+               if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
+                       return rtnl_group_changelink(skb, net,
                                                nla_get_u32(tb[IFLA_GROUP]),
                                                ifm, extack, tb);
-                       return -ENODEV;
-               }
+               return -ENODEV;
+       }
 
-               if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
-                       return -EOPNOTSUPP;
+       if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
+               return -EOPNOTSUPP;
 
-               if (!ops) {
+       if (!ops) {
 #ifdef CONFIG_MODULES
-                       if (kind[0]) {
-                               __rtnl_unlock();
-                               request_module("rtnl-link-%s", kind);
-                               rtnl_lock();
-                               ops = rtnl_link_ops_get(kind);
-                               if (ops)
-                                       goto replay;
-                       }
-#endif
-                       return -EOPNOTSUPP;
+               if (kind[0]) {
+                       __rtnl_unlock();
+                       request_module("rtnl-link-%s", kind);
+                       rtnl_lock();
+                       ops = rtnl_link_ops_get(kind);
+                       if (ops)
+                               goto replay;
                }
+#endif
+               NL_SET_ERR_MSG(extack, "Unknown device type");
+               return -EOPNOTSUPP;
+       }
 
-               if (!ops->setup)
-                       return -EOPNOTSUPP;
-
-               if (!ifname[0]) {
-                       snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
-                       name_assign_type = NET_NAME_ENUM;
-               }
+       if (!ops->setup)
+               return -EOPNOTSUPP;
 
-               dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
-               if (IS_ERR(dest_net))
-                       return PTR_ERR(dest_net);
+       if (!ifname[0]) {
+               snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
+               name_assign_type = NET_NAME_ENUM;
+       }
 
-               if (tb[IFLA_LINK_NETNSID]) {
-                       int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
+       dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
+       if (IS_ERR(dest_net))
+               return PTR_ERR(dest_net);
 
-                       link_net = get_net_ns_by_id(dest_net, id);
-                       if (!link_net) {
-                               err =  -EINVAL;
-                               goto out;
-                       }
-                       err = -EPERM;
-                       if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
-                               goto out;
-               }
+       if (tb[IFLA_LINK_NETNSID]) {
+               int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
 
-               dev = rtnl_create_link(link_net ? : dest_net, ifname,
-                                      name_assign_type, ops, tb);
-               if (IS_ERR(dev)) {
-                       err = PTR_ERR(dev);
+               link_net = get_net_ns_by_id(dest_net, id);
+               if (!link_net) {
+                       NL_SET_ERR_MSG(extack, "Unknown network namespace id");
+                       err =  -EINVAL;
                        goto out;
                }
+               err = -EPERM;
+               if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
+                       goto out;
+       } else {
+               link_net = NULL;
+       }
 
-               dev->ifindex = ifm->ifi_index;
+       dev = rtnl_create_link(link_net ? : dest_net, ifname,
+                              name_assign_type, ops, tb, extack);
+       if (IS_ERR(dev)) {
+               err = PTR_ERR(dev);
+               goto out;
+       }
 
-               if (ops->newlink) {
-                       err = ops->newlink(link_net ? : net, dev, tb, data,
-                                          extack);
-                       /* Drivers should call free_netdev() in ->destructor
-                        * and unregister it on failure after registration
-                        * so that device could be finally freed in rtnl_unlock.
-                        */
-                       if (err < 0) {
-                               /* If device is not registered at all, free it now */
-                               if (dev->reg_state == NETREG_UNINITIALIZED)
-                                       free_netdev(dev);
-                               goto out;
-                       }
-               } else {
-                       err = register_netdevice(dev);
-                       if (err < 0) {
+       dev->ifindex = ifm->ifi_index;
+
+       if (ops->newlink) {
+               err = ops->newlink(link_net ? : net, dev, tb, data, extack);
+               /* Drivers should call free_netdev() in ->destructor
+                * and unregister it on failure after registration
+                * so that device could be finally freed in rtnl_unlock.
+                */
+               if (err < 0) {
+                       /* If device is not registered at all, free it now */
+                       if (dev->reg_state == NETREG_UNINITIALIZED)
                                free_netdev(dev);
-                               goto out;
-                       }
+                       goto out;
+               }
+       } else {
+               err = register_netdevice(dev);
+               if (err < 0) {
+                       free_netdev(dev);
+                       goto out;
                }
-               err = rtnl_configure_link(dev, ifm);
+       }
+       err = rtnl_configure_link(dev, ifm);
+       if (err < 0)
+               goto out_unregister;
+       if (link_net) {
+               err = dev_change_net_namespace(dev, dest_net, ifname);
                if (err < 0)
                        goto out_unregister;
-               if (link_net) {
-                       err = dev_change_net_namespace(dev, dest_net, ifname);
-                       if (err < 0)
-                               goto out_unregister;
-               }
-               if (tb[IFLA_MASTER]) {
-                       err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]),
-                                           extack);
-                       if (err)
-                               goto out_unregister;
-               }
+       }
+       if (tb[IFLA_MASTER]) {
+               err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
+               if (err)
+                       goto out_unregister;
+       }
 out:
-               if (link_net)
-                       put_net(link_net);
-               put_net(dest_net);
-               return err;
+       if (link_net)
+               put_net(link_net);
+       put_net(dest_net);
+       return err;
 out_unregister:
-               if (ops->newlink) {
-                       LIST_HEAD(list_kill);
+       if (ops->newlink) {
+               LIST_HEAD(list_kill);
 
-                       ops->dellink(dev, &list_kill);
-                       unregister_netdevice_many(&list_kill);
-               } else {
-                       unregister_netdevice(dev);
-               }
-               goto out;
+               ops->dellink(dev, &list_kill);
+               unregister_netdevice_many(&list_kill);
+       } else {
+               unregister_netdevice(dev);
        }
+       goto out;
+}
+
+static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+                       struct netlink_ext_ack *extack)
+{
+       struct nlattr **attr;
+       int ret;
+
+       attr = kmalloc_array(RTNL_MAX_TYPE + 1, sizeof(*attr), GFP_KERNEL);
+       if (!attr)
+               return -ENOMEM;
+
+       ret = __rtnl_newlink(skb, nlh, attr, extack);
+       kfree(attr);
+       return ret;
 }
 
 static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
index a8217e2..4055254 100644 (file)
@@ -1089,7 +1089,7 @@ void sock_zerocopy_put(struct ubuf_info *uarg)
 }
 EXPORT_SYMBOL_GPL(sock_zerocopy_put);
 
-void sock_zerocopy_put_abort(struct ubuf_info *uarg)
+void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)
 {
        if (uarg) {
                struct sock *sk = skb_from_uarg(uarg)->sk;
@@ -1097,7 +1097,8 @@ void sock_zerocopy_put_abort(struct ubuf_info *uarg)
                atomic_dec(&sk->sk_zckey);
                uarg->len--;
 
-               sock_zerocopy_put(uarg);
+               if (have_uref)
+                       sock_zerocopy_put(uarg);
        }
 }
 EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort);
@@ -1105,6 +1106,12 @@ EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort);
 extern int __zerocopy_sg_from_iter(struct sock *sk, struct sk_buff *skb,
                                   struct iov_iter *from, size_t length);
 
+int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len)
+{
+       return __zerocopy_sg_from_iter(skb->sk, skb, &msg->msg_iter, len);
+}
+EXPORT_SYMBOL_GPL(skb_zerocopy_iter_dgram);
+
 int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                             struct msghdr *msg, int len,
                             struct ubuf_info *uarg)
@@ -1131,7 +1138,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
                return err;
        }
 
-       skb_zcopy_set(skb, uarg);
+       skb_zcopy_set(skb, uarg, NULL);
        return skb->len - orig_len;
 }
 EXPORT_SYMBOL_GPL(skb_zerocopy_iter_stream);
@@ -1151,7 +1158,7 @@ static int skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig,
                        if (skb_copy_ubufs(nskb, GFP_ATOMIC))
                                return -EIO;
                }
-               skb_zcopy_set(nskb, skb_uarg(orig));
+               skb_zcopy_set(nskb, skb_uarg(orig), NULL);
        }
        return 0;
 }
@@ -1925,8 +1932,6 @@ void *__pskb_pull_tail(struct sk_buff *skb, int delta)
                struct sk_buff *insp = NULL;
 
                do {
-                       BUG_ON(!list);
-
                        if (list->len <= eat) {
                                /* Eaten as whole. */
                                eat -= list->len;
@@ -2366,19 +2371,6 @@ error:
 }
 EXPORT_SYMBOL_GPL(skb_send_sock_locked);
 
-/* Send skb data on a socket. */
-int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len)
-{
-       int ret = 0;
-
-       lock_sock(sk);
-       ret = skb_send_sock_locked(sk, skb, offset, len);
-       release_sock(sk);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(skb_send_sock);
-
 /**
  *     skb_store_bits - store bits from kernel buffer to skb
  *     @skb: destination buffer
@@ -2645,6 +2637,65 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
 }
 EXPORT_SYMBOL(skb_copy_and_csum_bits);
 
+__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
+{
+       __sum16 sum;
+
+       sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
+       /* See comments in __skb_checksum_complete(). */
+       if (likely(!sum)) {
+               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+                   !skb->csum_complete_sw)
+                       netdev_rx_csum_fault(skb->dev, skb);
+       }
+       if (!skb_shared(skb))
+               skb->csum_valid = !sum;
+       return sum;
+}
+EXPORT_SYMBOL(__skb_checksum_complete_head);
+
+/* This function assumes skb->csum already holds pseudo header's checksum,
+ * which has been changed from the hardware checksum, for example, by
+ * __skb_checksum_validate_complete(). And, the original skb->csum must
+ * have been validated unsuccessfully for CHECKSUM_COMPLETE case.
+ *
+ * It returns non-zero if the recomputed checksum is still invalid, otherwise
+ * zero. The new checksum is stored back into skb->csum unless the skb is
+ * shared.
+ */
+__sum16 __skb_checksum_complete(struct sk_buff *skb)
+{
+       __wsum csum;
+       __sum16 sum;
+
+       csum = skb_checksum(skb, 0, skb->len, 0);
+
+       sum = csum_fold(csum_add(skb->csum, csum));
+       /* This check is inverted, because we already knew the hardware
+        * checksum is invalid before calling this function. So, if the
+        * re-computed checksum is valid instead, then we have a mismatch
+        * between the original skb->csum and skb_checksum(). This means either
+        * the original hardware checksum is incorrect or we screw up skb->csum
+        * when moving skb->data around.
+        */
+       if (likely(!sum)) {
+               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+                   !skb->csum_complete_sw)
+                       netdev_rx_csum_fault(skb->dev, skb);
+       }
+
+       if (!skb_shared(skb)) {
+               /* Save full packet checksum */
+               skb->csum = csum;
+               skb->ip_summed = CHECKSUM_COMPLETE;
+               skb->csum_complete_sw = 1;
+               skb->csum_valid = !sum;
+       }
+
+       return sum;
+}
+EXPORT_SYMBOL(__skb_checksum_complete);
+
 static __wsum warn_crc32c_csum_update(const void *buff, int len, __wsum sum)
 {
        net_warn_ratelimited(
@@ -2962,28 +3013,6 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head
 }
 EXPORT_SYMBOL(skb_append);
 
-/**
- *     skb_insert      -       insert a buffer
- *     @old: buffer to insert before
- *     @newsk: buffer to insert
- *     @list: list to use
- *
- *     Place a packet before a given packet in a list. The list locks are
- *     taken and this function is atomic with respect to other list locked
- *     calls.
- *
- *     A buffer cannot be placed on two lists at the same time.
- */
-void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&list->lock, flags);
-       __skb_insert(newsk, old->prev, old, list);
-       spin_unlock_irqrestore(&list->lock, flags);
-}
-EXPORT_SYMBOL(skb_insert);
-
 static inline void skb_split_inside_header(struct sk_buff *skb,
                                           struct sk_buff* skb1,
                                           const u32 len, const int pos)
@@ -4856,7 +4885,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 
 #ifdef CONFIG_NET_SWITCHDEV
        skb->offload_fwd_mark = 0;
-       skb->offload_mr_fwd_mark = 0;
+       skb->offload_l3_fwd_mark = 0;
 #endif
 
        if (!xnet)
@@ -5128,7 +5157,7 @@ int skb_vlan_pop(struct sk_buff *skb)
        int err;
 
        if (likely(skb_vlan_tag_present(skb))) {
-               skb->vlan_tci = 0;
+               __vlan_hwaccel_clear_tag(skb);
        } else {
                if (unlikely(!eth_type_vlan(skb->protocol)))
                        return 0;
index 080a880..f00902c 100644 (file)
@@ -567,6 +567,8 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
 
        lock_sock(sk);
        sk->sk_bound_dev_if = index;
+       if (sk->sk_prot->rehash)
+               sk->sk_prot->rehash(sk);
        sk_dst_reset(sk);
        release_sock(sk);
 
@@ -698,6 +700,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
                break;
        case SO_DONTROUTE:
                sock_valbool_flag(sk, SOCK_LOCALROUTE, valbool);
+               sk_dst_reset(sk);
                break;
        case SO_BROADCAST:
                sock_valbool_flag(sk, SOCK_BROADCAST, valbool);
@@ -950,10 +953,12 @@ set_rcvbuf:
                        clear_bit(SOCK_PASSSEC, &sock->flags);
                break;
        case SO_MARK:
-               if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+               if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
                        ret = -EPERM;
-               else
+               } else if (val != sk->sk_mark) {
                        sk->sk_mark = val;
+                       sk_dst_reset(sk);
+               }
                break;
 
        case SO_RXQ_OVFL:
@@ -1014,7 +1019,10 @@ set_rcvbuf:
 
        case SO_ZEROCOPY:
                if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6) {
-                       if (sk->sk_protocol != IPPROTO_TCP)
+                       if (!((sk->sk_type == SOCK_STREAM &&
+                              sk->sk_protocol == IPPROTO_TCP) ||
+                             (sk->sk_type == SOCK_DGRAM &&
+                              sk->sk_protocol == IPPROTO_UDP)))
                                ret = -ENOTSUPP;
                } else if (sk->sk_family != PF_RDS) {
                        ret = -ENOTSUPP;
index ba5cba5..d8fe3e5 100644 (file)
@@ -187,6 +187,7 @@ int reuseport_add_sock(struct sock *sk, struct sock *sk2, bool bind_inany)
                call_rcu(&old_reuse->rcu, reuseport_free_rcu);
        return 0;
 }
+EXPORT_SYMBOL(reuseport_add_sock);
 
 void reuseport_detach_sock(struct sock *sk)
 {
index 7d329fb..e94bb02 100644 (file)
@@ -32,7 +32,7 @@ void sk_stream_write_space(struct sock *sk)
        struct socket *sock = sk->sk_socket;
        struct socket_wq *wq;
 
-       if (sk_stream_is_writeable(sk) && sock) {
+       if (__sk_stream_is_writeable(sk, 1) && sock) {
                clear_bit(SOCK_NOSPACE, &sock->flags);
 
                rcu_read_lock();
index 8e08cea..26a21d9 100644 (file)
@@ -231,7 +231,7 @@ EXPORT_SYMBOL(dccp_req_err);
  * check at all. A more general error queue to queue errors for later handling
  * is probably better.
  */
-static void dccp_v4_err(struct sk_buff *skb, u32 info)
+static int dccp_v4_err(struct sk_buff *skb, u32 info)
 {
        const struct iphdr *iph = (struct iphdr *)skb->data;
        const u8 offset = iph->ihl << 2;
@@ -259,16 +259,18 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
                                       inet_iif(skb), 0);
        if (!sk) {
                __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
-               return;
+               return -ENOENT;
        }
 
        if (sk->sk_state == DCCP_TIME_WAIT) {
                inet_twsk_put(inet_twsk(sk));
-               return;
+               return 0;
        }
        seq = dccp_hdr_seq(dh);
-       if (sk->sk_state == DCCP_NEW_SYN_RECV)
-               return dccp_req_err(sk, seq);
+       if (sk->sk_state == DCCP_NEW_SYN_RECV) {
+               dccp_req_err(sk, seq);
+               return 0;
+       }
 
        bh_lock_sock(sk);
        /* If too many ICMPs get dropped on busy
@@ -357,6 +359,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
+       return 0;
 }
 
 static inline __sum16 dccp_v4_csum_finish(struct sk_buff *skb,
index 6344f1b..d5740ba 100644 (file)
@@ -68,7 +68,7 @@ static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)
 
 }
 
-static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        u8 type, u8 code, int offset, __be32 info)
 {
        const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;
@@ -96,16 +96,18 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        if (!sk) {
                __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
                                  ICMP6_MIB_INERRORS);
-               return;
+               return -ENOENT;
        }
 
        if (sk->sk_state == DCCP_TIME_WAIT) {
                inet_twsk_put(inet_twsk(sk));
-               return;
+               return 0;
        }
        seq = dccp_hdr_seq(dh);
-       if (sk->sk_state == DCCP_NEW_SYN_RECV)
-               return dccp_req_err(sk, seq);
+       if (sk->sk_state == DCCP_NEW_SYN_RECV) {
+               dccp_req_err(sk, seq);
+               return 0;
+       }
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk))
@@ -183,6 +185,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
+       return 0;
 }
 
 
index 43733ac..658cd32 100644 (file)
@@ -948,6 +948,7 @@ int inet_dccp_listen(struct socket *sock, int backlog)
        if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN)))
                goto out;
 
+       sk->sk_max_ack_backlog = backlog;
        /* Really, if the socket is already in listen state
         * we can only allow the backlog to be adjusted.
         */
@@ -960,7 +961,6 @@ int inet_dccp_listen(struct socket *sock, int backlog)
                if (err)
                        goto out;
        }
-       sk->sk_max_ack_backlog = backlog;
        err = 0;
 
 out:
index 7d6ff98..7aab5d0 100644 (file)
@@ -192,7 +192,7 @@ static int check_port(__le16 port)
 static unsigned short port_alloc(struct sock *sk)
 {
        struct dn_scp *scp = DN_SK(sk);
-static unsigned short port = 0x2000;
+       static unsigned short port = 0x2000;
        unsigned short i_port = port;
 
        while(check_port(cpu_to_le16(++port)) != 0) {
index 5e8c9be..71bb15f 100644 (file)
@@ -179,10 +179,38 @@ static const struct attribute_group dsa_group = {
        .attrs  = dsa_slave_attrs,
 };
 
+static void dsa_master_set_mtu(struct net_device *dev, struct dsa_port *cpu_dp)
+{
+       unsigned int mtu = ETH_DATA_LEN + cpu_dp->tag_ops->overhead;
+       int err;
+
+       rtnl_lock();
+       if (mtu <= dev->max_mtu) {
+               err = dev_set_mtu(dev, mtu);
+               if (err)
+                       netdev_dbg(dev, "Unable to set MTU to include for DSA overheads\n");
+       }
+       rtnl_unlock();
+}
+
+static void dsa_master_reset_mtu(struct net_device *dev)
+{
+       int err;
+
+       rtnl_lock();
+       err = dev_set_mtu(dev, ETH_DATA_LEN);
+       if (err)
+               netdev_dbg(dev,
+                          "Unable to reset MTU to exclude DSA overheads\n");
+       rtnl_unlock();
+}
+
 int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
 {
        int ret;
 
+       dsa_master_set_mtu(dev,  cpu_dp);
+
        /* If we use a tagging format that doesn't have an ethertype
         * field, make sure that all packets from this point on get
         * sent to the tag format's receive function.
@@ -206,6 +234,7 @@ void dsa_master_teardown(struct net_device *dev)
 {
        sysfs_remove_group(&dev->dev.kobj, &dsa_group);
        dsa_master_ethtool_teardown(dev);
+       dsa_master_reset_mtu(dev);
 
        dev->dsa_ptr = NULL;
 
index ed05954..2d7e01b 100644 (file)
@@ -252,9 +252,6 @@ int dsa_port_vlan_add(struct dsa_port *dp,
                .vlan = vlan,
        };
 
-       if (netif_is_bridge_master(vlan->obj.orig_dev))
-               return -EOPNOTSUPP;
-
        if (br_vlan_enabled(dp->bridge_dev))
                return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
 
index aec78f5..a3fcc1d 100644 (file)
@@ -1050,8 +1050,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
 static const struct switchdev_ops dsa_slave_switchdev_ops = {
        .switchdev_port_attr_get        = dsa_slave_port_attr_get,
        .switchdev_port_attr_set        = dsa_slave_port_attr_set,
-       .switchdev_port_obj_add         = dsa_slave_port_obj_add,
-       .switchdev_port_obj_del         = dsa_slave_port_obj_del,
 };
 
 static struct device_type dsa_type = {
@@ -1529,6 +1527,44 @@ err_fdb_work_init:
        return NOTIFY_BAD;
 }
 
+static int
+dsa_slave_switchdev_port_obj_event(unsigned long event,
+                       struct net_device *netdev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD:
+               err = dsa_slave_port_obj_add(netdev, port_obj_info->obj,
+                                            port_obj_info->trans);
+               break;
+       case SWITCHDEV_PORT_OBJ_DEL:
+               err = dsa_slave_port_obj_del(netdev, port_obj_info->obj);
+               break;
+       }
+
+       port_obj_info->handled = true;
+       return notifier_from_errno(err);
+}
+
+static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
+                                             unsigned long event, void *ptr)
+{
+       struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+       if (!dsa_slave_dev_check(dev))
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case SWITCHDEV_PORT_OBJ_ADD: /* fall through */
+       case SWITCHDEV_PORT_OBJ_DEL:
+               return dsa_slave_switchdev_port_obj_event(event, dev, ptr);
+       }
+
+       return NOTIFY_DONE;
+}
+
 static struct notifier_block dsa_slave_nb __read_mostly = {
        .notifier_call  = dsa_slave_netdevice_event,
 };
@@ -1537,8 +1573,13 @@ static struct notifier_block dsa_slave_switchdev_notifier = {
        .notifier_call = dsa_slave_switchdev_event,
 };
 
+static struct notifier_block dsa_slave_switchdev_blocking_notifier = {
+       .notifier_call = dsa_slave_switchdev_blocking_event,
+};
+
 int dsa_slave_register_notifier(void)
 {
+       struct notifier_block *nb;
        int err;
 
        err = register_netdevice_notifier(&dsa_slave_nb);
@@ -1549,8 +1590,15 @@ int dsa_slave_register_notifier(void)
        if (err)
                goto err_switchdev_nb;
 
+       nb = &dsa_slave_switchdev_blocking_notifier;
+       err = register_switchdev_blocking_notifier(nb);
+       if (err)
+               goto err_switchdev_blocking_nb;
+
        return 0;
 
+err_switchdev_blocking_nb:
+       unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
 err_switchdev_nb:
        unregister_netdevice_notifier(&dsa_slave_nb);
        return err;
@@ -1558,8 +1606,14 @@ err_switchdev_nb:
 
 void dsa_slave_unregister_notifier(void)
 {
+       struct notifier_block *nb;
        int err;
 
+       nb = &dsa_slave_switchdev_blocking_notifier;
+       err = unregister_switchdev_blocking_notifier(nb);
+       if (err)
+               pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err);
+
        err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
        if (err)
                pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err);
index 2b06bb9..4aa1d36 100644 (file)
@@ -174,6 +174,7 @@ static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops brcm_netdev_ops = {
        .xmit   = brcm_tag_xmit,
        .rcv    = brcm_tag_rcv,
+       .overhead = BRCM_TAG_LEN,
 };
 #endif
 
@@ -196,5 +197,6 @@ static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb,
 const struct dsa_device_ops brcm_prepend_netdev_ops = {
        .xmit   = brcm_tag_xmit_prepend,
        .rcv    = brcm_tag_rcv_prepend,
+       .overhead = BRCM_TAG_LEN,
 };
 #endif
index cd13cfc..8b2f92e 100644 (file)
@@ -149,4 +149,5 @@ static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops dsa_netdev_ops = {
        .xmit   = dsa_xmit,
        .rcv    = dsa_rcv,
+       .overhead = DSA_HLEN,
 };
index 4083326..f5b87ee 100644 (file)
@@ -168,4 +168,5 @@ static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops edsa_netdev_ops = {
        .xmit   = edsa_xmit,
        .rcv    = edsa_rcv,
+       .overhead = EDSA_HLEN,
 };
index 49e9b73..cb6f82f 100644 (file)
@@ -106,4 +106,5 @@ static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb,
 const struct dsa_device_ops gswip_netdev_ops = {
        .xmit = gswip_tag_xmit,
        .rcv = gswip_tag_rcv,
+       .overhead = GSWIP_RX_HEADER_LEN,
 };
index 0f62eff..96411f7 100644 (file)
@@ -99,4 +99,5 @@ static struct sk_buff *ksz_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops ksz_netdev_ops = {
        .xmit   = ksz_xmit,
        .rcv    = ksz_rcv,
+       .overhead = KSZ_INGRESS_TAG_LEN,
 };
index 548c002..f48889e 100644 (file)
@@ -140,4 +140,5 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops lan9303_netdev_ops = {
        .xmit = lan9303_xmit,
        .rcv = lan9303_rcv,
+       .overhead = LAN9303_TAG_LEN,
 };
index 11535bc..f39f4df 100644 (file)
@@ -109,4 +109,5 @@ const struct dsa_device_ops mtk_netdev_ops = {
        .xmit           = mtk_tag_xmit,
        .rcv            = mtk_tag_rcv,
        .flow_dissect   = mtk_tag_flow_dissect,
+       .overhead       = MTK_HDR_LEN,
 };
index 613f4ee..ed4f6dc 100644 (file)
@@ -101,4 +101,5 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops qca_netdev_ops = {
        .xmit   = qca_tag_xmit,
        .rcv    = qca_tag_rcv,
+       .overhead = QCA_HDR_LEN,
 };
index 56197f0..b40756e 100644 (file)
@@ -84,4 +84,5 @@ static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev,
 const struct dsa_device_ops trailer_netdev_ops = {
        .xmit   = trailer_xmit,
        .rcv    = trailer_rcv,
+       .overhead = 4,
 };
index fd8faa0..4c52011 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/inet.h>
 #include <linux/ip.h>
 #include <linux/netdevice.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
@@ -165,15 +166,17 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
        eth = (struct ethhdr *)skb->data;
        skb_pull_inline(skb, ETH_HLEN);
 
-       if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) {
-               if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
-                       skb->pkt_type = PACKET_BROADCAST;
-               else
-                       skb->pkt_type = PACKET_MULTICAST;
+       if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+                                             dev->dev_addr))) {
+               if (unlikely(is_multicast_ether_addr_64bits(eth->h_dest))) {
+                       if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
+                               skb->pkt_type = PACKET_BROADCAST;
+                       else
+                               skb->pkt_type = PACKET_MULTICAST;
+               } else {
+                       skb->pkt_type = PACKET_OTHERHOST;
+               }
        }
-       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
-                                                  dev->dev_addr)))
-               skb->pkt_type = PACKET_OTHERHOST;
 
        /*
         * Some variants of DSA tagging don't have an ethertype field
@@ -548,3 +551,40 @@ int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
        return 0;
 }
 EXPORT_SYMBOL(eth_platform_get_mac_address);
+
+/**
+ * Obtain the MAC address from an nvmem cell named 'mac-address' associated
+ * with given device.
+ *
+ * @dev:       Device with which the mac-address cell is associated.
+ * @addrbuf:   Buffer to which the MAC address will be copied on success.
+ *
+ * Returns 0 on success or a negative error number on failure.
+ */
+int nvmem_get_mac_address(struct device *dev, void *addrbuf)
+{
+       struct nvmem_cell *cell;
+       const void *mac;
+       size_t len;
+
+       cell = nvmem_cell_get(dev, "mac-address");
+       if (IS_ERR(cell))
+               return PTR_ERR(cell);
+
+       mac = nvmem_cell_read(cell, &len);
+       nvmem_cell_put(cell);
+
+       if (IS_ERR(mac))
+               return PTR_ERR(mac);
+
+       if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
+               kfree(mac);
+               return -EINVAL;
+       }
+
+       ether_addr_copy(addrbuf, mac);
+       kfree(mac);
+
+       return 0;
+}
+EXPORT_SYMBOL(nvmem_get_mac_address);
index 1fbe2f8..326c422 100644 (file)
@@ -208,6 +208,7 @@ int inet_listen(struct socket *sock, int backlog)
        if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
                goto out;
 
+       sk->sk_max_ack_backlog = backlog;
        /* Really, if the socket is already in listen state
         * we can only allow the backlog to be adjusted.
         */
@@ -231,7 +232,6 @@ int inet_listen(struct socket *sock, int backlog)
                        goto out;
                tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_LISTEN_CB, 0, NULL);
        }
-       sk->sk_max_ack_backlog = backlog;
        err = 0;
 
 out:
@@ -1964,6 +1964,8 @@ static int __init inet_init(void)
        /* Add UDP-Lite (RFC 3828) */
        udplite4_register();
 
+       raw_init();
+
        ping_init();
 
        /*
index a34602a..5b9b6d4 100644 (file)
@@ -1100,7 +1100,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
                                inet_del_ifa(in_dev, ifap, 1);
                        break;
                }
-               ret = dev_change_flags(dev, ifr->ifr_flags);
+               ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
                break;
 
        case SIOCSIFADDR:       /* Set interface address (and family) */
index b5c3937..5022bc6 100644 (file)
@@ -1076,7 +1076,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
        if (!fi)
                goto failure;
        fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx,
-                                             cfg->fc_mx_len);
+                                             cfg->fc_mx_len, extack);
        if (unlikely(IS_ERR(fi->fib_metrics))) {
                err = PTR_ERR(fi->fib_metrics);
                kfree(fi);
index 500a599..0d0ad19 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/socket.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
+#include <linux/icmp.h>
 #include <linux/udp.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -1003,15 +1004,82 @@ static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
        return 0;
 }
 
+static int gue_err_proto_handler(int proto, struct sk_buff *skb, u32 info)
+{
+       const struct net_protocol *ipprot = rcu_dereference(inet_protos[proto]);
+
+       if (ipprot && ipprot->err_handler) {
+               if (!ipprot->err_handler(skb, info))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int gue_err(struct sk_buff *skb, u32 info)
+{
+       int transport_offset = skb_transport_offset(skb);
+       struct guehdr *guehdr;
+       size_t optlen;
+       int ret;
+
+       if (skb->len < sizeof(struct udphdr) + sizeof(struct guehdr))
+               return -EINVAL;
+
+       guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+       switch (guehdr->version) {
+       case 0: /* Full GUE header present */
+               break;
+       case 1: {
+               /* Direct encasulation of IPv4 or IPv6 */
+               skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
+
+               switch (((struct iphdr *)guehdr)->version) {
+               case 4:
+                       ret = gue_err_proto_handler(IPPROTO_IPIP, skb, info);
+                       goto out;
+#if IS_ENABLED(CONFIG_IPV6)
+               case 6:
+                       ret = gue_err_proto_handler(IPPROTO_IPV6, skb, info);
+                       goto out;
+#endif
+               default:
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
+       }
+       default: /* Undefined version */
+               return -EOPNOTSUPP;
+       }
+
+       if (guehdr->control)
+               return -ENOENT;
+
+       optlen = guehdr->hlen << 2;
+
+       if (validate_gue_flags(guehdr, optlen))
+               return -EINVAL;
+
+       skb_set_transport_header(skb, -(int)sizeof(struct icmphdr));
+       ret = gue_err_proto_handler(guehdr->proto_ctype, skb, info);
+
+out:
+       skb_set_transport_header(skb, transport_offset);
+       return ret;
+}
+
 
 static const struct ip_tunnel_encap_ops fou_iptun_ops = {
        .encap_hlen = fou_encap_hlen,
        .build_header = fou_build_header,
+       .err_handler = gue_err,
 };
 
 static const struct ip_tunnel_encap_ops gue_iptun_ops = {
        .encap_hlen = gue_encap_hlen,
        .build_header = gue_build_header,
+       .err_handler = gue_err,
 };
 
 static int ip_tunnel_encap_add_fou_ops(void)
index 7efe740..a4bf22e 100644 (file)
@@ -151,20 +151,25 @@ drop:
        return NET_RX_DROP;
 }
 
-static void gre_err(struct sk_buff *skb, u32 info)
+static int gre_err(struct sk_buff *skb, u32 info)
 {
        const struct gre_protocol *proto;
        const struct iphdr *iph = (const struct iphdr *)skb->data;
        u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
+       int err = 0;
 
        if (ver >= GREPROTO_MAX)
-               return;
+               return -EINVAL;
 
        rcu_read_lock();
        proto = rcu_dereference(gre_proto[ver]);
        if (proto && proto->err_handler)
                proto->err_handler(skb, info);
+       else
+               err = -EPROTONOSUPPORT;
        rcu_read_unlock();
+
+       return err;
 }
 
 static const struct net_protocol net_gre_protocol = {
index d832bee..065997f 100644 (file)
@@ -1079,7 +1079,7 @@ error:
        goto drop;
 }
 
-void icmp_err(struct sk_buff *skb, u32 info)
+int icmp_err(struct sk_buff *skb, u32 info)
 {
        struct iphdr *iph = (struct iphdr *)skb->data;
        int offset = iph->ihl<<2;
@@ -1094,13 +1094,15 @@ void icmp_err(struct sk_buff *skb, u32 info)
         */
        if (icmph->type != ICMP_ECHOREPLY) {
                ping_err(skb, offset, info);
-               return;
+               return 0;
        }
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
                ipv4_update_pmtu(skb, net, info, 0, IPPROTO_ICMP);
        else if (type == ICMP_REDIRECT)
                ipv4_redirect(skb, net, 0, IPPROTO_ICMP);
+
+       return 0;
 }
 
 /*
index 15e7f79..6ea523d 100644 (file)
@@ -183,7 +183,9 @@ inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int *
        int i, low, high, attempt_half;
        struct inet_bind_bucket *tb;
        u32 remaining, offset;
+       int l3mdev;
 
+       l3mdev = inet_sk_bound_l3mdev(sk);
        attempt_half = (sk->sk_reuse == SK_CAN_REUSE) ? 1 : 0;
 other_half_scan:
        inet_get_local_port_range(net, &low, &high);
@@ -219,7 +221,8 @@ other_parity_scan:
                                                  hinfo->bhash_size)];
                spin_lock_bh(&head->lock);
                inet_bind_bucket_for_each(tb, &head->chain)
-                       if (net_eq(ib_net(tb), net) && tb->port == port) {
+                       if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
+                           tb->port == port) {
                                if (!inet_csk_bind_conflict(sk, tb, false, false))
                                        goto success;
                                goto next_port;
@@ -293,6 +296,9 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
        struct net *net = sock_net(sk);
        struct inet_bind_bucket *tb = NULL;
        kuid_t uid = sock_i_uid(sk);
+       int l3mdev;
+
+       l3mdev = inet_sk_bound_l3mdev(sk);
 
        if (!port) {
                head = inet_csk_find_open_port(sk, &tb, &port);
@@ -306,11 +312,12 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
                                          hinfo->bhash_size)];
        spin_lock_bh(&head->lock);
        inet_bind_bucket_for_each(tb, &head->chain)
-               if (net_eq(ib_net(tb), net) && tb->port == port)
+               if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
+                   tb->port == port)
                        goto tb_found;
 tb_not_found:
        tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
-                                    net, head, port);
+                                    net, head, port, l3mdev);
        if (!tb)
                goto fail_unlock;
 tb_found:
@@ -874,7 +881,6 @@ int inet_csk_listen_start(struct sock *sk, int backlog)
 
        reqsk_queue_alloc(&icsk->icsk_accept_queue);
 
-       sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
        inet_csk_delack_init(sk);
 
index 411dd7a..13890d5 100644 (file)
@@ -65,12 +65,14 @@ static u32 sk_ehashfn(const struct sock *sk)
 struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
                                                 struct net *net,
                                                 struct inet_bind_hashbucket *head,
-                                                const unsigned short snum)
+                                                const unsigned short snum,
+                                                int l3mdev)
 {
        struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
 
        if (tb) {
                write_pnet(&tb->ib_net, net);
+               tb->l3mdev    = l3mdev;
                tb->port      = snum;
                tb->fastreuse = 0;
                tb->fastreuseport = 0;
@@ -135,6 +137,7 @@ int __inet_inherit_port(const struct sock *sk, struct sock *child)
                        table->bhash_size);
        struct inet_bind_hashbucket *head = &table->bhash[bhash];
        struct inet_bind_bucket *tb;
+       int l3mdev;
 
        spin_lock(&head->lock);
        tb = inet_csk(sk)->icsk_bind_hash;
@@ -143,6 +146,8 @@ int __inet_inherit_port(const struct sock *sk, struct sock *child)
                return -ENOENT;
        }
        if (tb->port != port) {
+               l3mdev = inet_sk_bound_l3mdev(sk);
+
                /* NOTE: using tproxy and redirecting skbs to a proxy
                 * on a different listener port breaks the assumption
                 * that the listener socket's icsk_bind_hash is the same
@@ -150,12 +155,13 @@ int __inet_inherit_port(const struct sock *sk, struct sock *child)
                 * create a new bind bucket for the child here. */
                inet_bind_bucket_for_each(tb, &head->chain) {
                        if (net_eq(ib_net(tb), sock_net(sk)) &&
-                           tb->port == port)
+                           tb->l3mdev == l3mdev && tb->port == port)
                                break;
                }
                if (!tb) {
                        tb = inet_bind_bucket_create(table->bind_bucket_cachep,
-                                                    sock_net(sk), head, port);
+                                                    sock_net(sk), head, port,
+                                                    l3mdev);
                        if (!tb) {
                                spin_unlock(&head->lock);
                                return -ENOMEM;
@@ -229,6 +235,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
 {
        int score = -1;
        struct inet_sock *inet = inet_sk(sk);
+       bool dev_match;
 
        if (net_eq(sock_net(sk), net) && inet->inet_num == hnum &&
                        !ipv6_only_sock(sk)) {
@@ -239,15 +246,12 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score += 4;
                }
-               if (sk->sk_bound_dev_if || exact_dif) {
-                       bool dev_match = (sk->sk_bound_dev_if == dif ||
-                                         sk->sk_bound_dev_if == sdif);
+               dev_match = inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+                                                dif, sdif);
+               if (!dev_match)
+                       return -1;
+               score += 4;
 
-                       if (!dev_match)
-                               return -1;
-                       if (sk->sk_bound_dev_if)
-                               score += 4;
-               }
                if (sk->sk_incoming_cpu == raw_smp_processor_id())
                        score++;
        }
@@ -675,6 +679,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
        u32 remaining, offset;
        int ret, i, low, high;
        static u32 hint;
+       int l3mdev;
 
        if (port) {
                head = &hinfo->bhash[inet_bhashfn(net, port,
@@ -693,6 +698,8 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                return ret;
        }
 
+       l3mdev = inet_sk_bound_l3mdev(sk);
+
        inet_get_local_port_range(net, &low, &high);
        high++; /* [32768, 60999] -> [32768, 61000[ */
        remaining = high - low;
@@ -719,7 +726,8 @@ other_parity_scan:
                 * the established check is already unique enough.
                 */
                inet_bind_bucket_for_each(tb, &head->chain) {
-                       if (net_eq(ib_net(tb), net) && tb->port == port) {
+                       if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
+                           tb->port == port) {
                                if (tb->fastreuse >= 0 ||
                                    tb->fastreuseport >= 0)
                                        goto next_port;
@@ -732,7 +740,7 @@ other_parity_scan:
                }
 
                tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
-                                            net, head, port);
+                                            net, head, port, l3mdev);
                if (!tb) {
                        spin_unlock_bh(&head->lock);
                        return -ENOMEM;
index 32662e9..06ee469 100644 (file)
@@ -69,6 +69,13 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s
        __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS);
        __IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       if (skb->offload_l3_fwd_mark) {
+               consume_skb(skb);
+               return 0;
+       }
+#endif
+
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
 
index 38befe8..76a9a5f 100644 (file)
@@ -121,8 +121,8 @@ static unsigned int ipgre_net_id __read_mostly;
 static unsigned int gre_tap_net_id __read_mostly;
 static unsigned int erspan_net_id __read_mostly;
 
-static void ipgre_err(struct sk_buff *skb, u32 info,
-                     const struct tnl_ptk_info *tpi)
+static int ipgre_err(struct sk_buff *skb, u32 info,
+                    const struct tnl_ptk_info *tpi)
 {
 
        /* All the routers (except for Linux) return only
@@ -146,17 +146,32 @@ static void ipgre_err(struct sk_buff *skb, u32 info,
        unsigned int data_len = 0;
        struct ip_tunnel *t;
 
+       if (tpi->proto == htons(ETH_P_TEB))
+               itn = net_generic(net, gre_tap_net_id);
+       else if (tpi->proto == htons(ETH_P_ERSPAN) ||
+                tpi->proto == htons(ETH_P_ERSPAN2))
+               itn = net_generic(net, erspan_net_id);
+       else
+               itn = net_generic(net, ipgre_net_id);
+
+       iph = (const struct iphdr *)(icmp_hdr(skb) + 1);
+       t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
+                            iph->daddr, iph->saddr, tpi->key);
+
+       if (!t)
+               return -ENOENT;
+
        switch (type) {
        default:
        case ICMP_PARAMETERPROB:
-               return;
+               return 0;
 
        case ICMP_DEST_UNREACH:
                switch (code) {
                case ICMP_SR_FAILED:
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
-                       return;
+                       return 0;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -168,7 +183,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info,
 
        case ICMP_TIME_EXCEEDED:
                if (code != ICMP_EXC_TTL)
-                       return;
+                       return 0;
                data_len = icmp_hdr(skb)->un.reserved[1] * 4; /* RFC 4884 4.1 */
                break;
 
@@ -176,40 +191,27 @@ static void ipgre_err(struct sk_buff *skb, u32 info,
                break;
        }
 
-       if (tpi->proto == htons(ETH_P_TEB))
-               itn = net_generic(net, gre_tap_net_id);
-       else if (tpi->proto == htons(ETH_P_ERSPAN) ||
-                tpi->proto == htons(ETH_P_ERSPAN2))
-               itn = net_generic(net, erspan_net_id);
-       else
-               itn = net_generic(net, ipgre_net_id);
-
-       iph = (const struct iphdr *)(icmp_hdr(skb) + 1);
-       t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
-                            iph->daddr, iph->saddr, tpi->key);
-
-       if (!t)
-               return;
-
 #if IS_ENABLED(CONFIG_IPV6)
        if (tpi->proto == htons(ETH_P_IPV6) &&
            !ip6_err_gen_icmpv6_unreach(skb, iph->ihl * 4 + tpi->hdr_len,
                                       type, data_len))
-               return;
+               return 0;
 #endif
 
        if (t->parms.iph.daddr == 0 ||
            ipv4_is_multicast(t->parms.iph.daddr))
-               return;
+               return 0;
 
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
-               return;
+               return 0;
 
        if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
                t->err_count++;
        else
                t->err_count = 1;
        t->err_time = jiffies;
+
+       return 0;
 }
 
 static void gre_err(struct sk_buff *skb, u32 info)
@@ -1601,7 +1603,7 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
        memset(&tb, 0, sizeof(tb));
 
        dev = rtnl_create_link(net, name, name_assign_type,
-                              &ipgre_tap_ops, tb);
+                              &ipgre_tap_ops, tb, NULL);
        if (IS_ERR(dev))
                return dev;
 
index e609b08..26921f6 100644 (file)
@@ -188,51 +188,50 @@ bool ip_call_ra_chain(struct sk_buff *skb)
        return false;
 }
 
-static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol)
 {
-       __skb_pull(skb, skb_network_header_len(skb));
-
-       rcu_read_lock();
-       {
-               int protocol = ip_hdr(skb)->protocol;
-               const struct net_protocol *ipprot;
-               int raw;
+       const struct net_protocol *ipprot;
+       int raw, ret;
 
-       resubmit:
-               raw = raw_local_deliver(skb, protocol);
+resubmit:
+       raw = raw_local_deliver(skb, protocol);
 
-               ipprot = rcu_dereference(inet_protos[protocol]);
-               if (ipprot) {
-                       int ret;
-
-                       if (!ipprot->no_policy) {
-                               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-                                       kfree_skb(skb);
-                                       goto out;
-                               }
-                               nf_reset(skb);
+       ipprot = rcu_dereference(inet_protos[protocol]);
+       if (ipprot) {
+               if (!ipprot->no_policy) {
+                       if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+                               kfree_skb(skb);
+                               return;
                        }
-                       ret = ipprot->handler(skb);
-                       if (ret < 0) {
-                               protocol = -ret;
-                               goto resubmit;
+                       nf_reset(skb);
+               }
+               ret = ipprot->handler(skb);
+               if (ret < 0) {
+                       protocol = -ret;
+                       goto resubmit;
+               }
+               __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
+       } else {
+               if (!raw) {
+                       if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+                               __IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);
+                               icmp_send(skb, ICMP_DEST_UNREACH,
+                                         ICMP_PROT_UNREACH, 0);
                        }
-                       __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
+                       kfree_skb(skb);
                } else {
-                       if (!raw) {
-                               if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-                                       __IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);
-                                       icmp_send(skb, ICMP_DEST_UNREACH,
-                                                 ICMP_PROT_UNREACH, 0);
-                               }
-                               kfree_skb(skb);
-                       } else {
-                               __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
-                               consume_skb(skb);
-                       }
+                       __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
+                       consume_skb(skb);
                }
        }
- out:
+}
+
+static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       __skb_pull(skb, skb_network_header_len(skb));
+
+       rcu_read_lock();
+       ip_protocol_deliver_rcu(net, skb, ip_hdr(skb)->protocol);
        rcu_read_unlock();
 
        return 0;
index 5dbec21..ab66180 100644 (file)
@@ -867,6 +867,7 @@ static int __ip_append_data(struct sock *sk,
                            unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
+       struct ubuf_info *uarg = NULL;
        struct sk_buff *skb;
 
        struct ip_options *opt = cork->opt;
@@ -880,8 +881,8 @@ static int __ip_append_data(struct sock *sk,
        int csummode = CHECKSUM_NONE;
        struct rtable *rt = (struct rtable *)cork->dst;
        unsigned int wmem_alloc_delta = 0;
+       bool paged, extra_uref;
        u32 tskey = 0;
-       bool paged;
 
        skb = skb_peek_tail(queue);
 
@@ -916,6 +917,20 @@ static int __ip_append_data(struct sock *sk,
            (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM)))
                csummode = CHECKSUM_PARTIAL;
 
+       if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
+               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               if (!uarg)
+                       return -ENOBUFS;
+               extra_uref = true;
+               if (rt->dst.dev->features & NETIF_F_SG &&
+                   csummode == CHECKSUM_PARTIAL) {
+                       paged = true;
+               } else {
+                       uarg->zerocopy = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+               }
+       }
+
        cork->length += length;
 
        /* So, what's going on in the loop below?
@@ -1001,12 +1016,6 @@ alloc_new_skb:
                        skb->csum = 0;
                        skb_reserve(skb, hh_len);
 
-                       /* only the initial fragment is time stamped */
-                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
-                       cork->tx_flags = 0;
-                       skb_shinfo(skb)->tskey = tskey;
-                       tskey = 0;
-
                        /*
                         *      Find where to start putting bytes.
                         */
@@ -1039,6 +1048,13 @@ alloc_new_skb:
                        exthdrlen = 0;
                        csummode = CHECKSUM_NONE;
 
+                       /* only the initial fragment is time stamped */
+                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
+                       cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+
                        if ((flags & MSG_CONFIRM) && !skb_prev)
                                skb_set_dst_pending_confirm(skb, 1);
 
@@ -1068,7 +1084,7 @@ alloc_new_skb:
                                err = -EFAULT;
                                goto error;
                        }
-               } else {
+               } else if (!uarg || !uarg->zerocopy) {
                        int i = skb_shinfo(skb)->nr_frags;
 
                        err = -ENOMEM;
@@ -1098,6 +1114,10 @@ alloc_new_skb:
                        skb->data_len += copy;
                        skb->truesize += copy;
                        wmem_alloc_delta += copy;
+               } else {
+                       err = skb_zerocopy_iter_dgram(skb, from, copy);
+                       if (err < 0)
+                               goto error;
                }
                offset += copy;
                length -= copy;
@@ -1110,6 +1130,8 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
+       if (uarg)
+               sock_zerocopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index c248e0d..c857ec6 100644 (file)
@@ -120,7 +120,7 @@ int __iptunnel_pull_header(struct sk_buff *skb, int hdr_len,
        }
 
        skb_clear_hash_if_not_l4(skb);
-       skb->vlan_tci = 0;
+       __vlan_hwaccel_clear_tag(skb);
        skb_set_queue_mapping(skb, 0);
        skb_scrub_packet(skb, xnet);
 
index 8821261..5575776 100644 (file)
@@ -220,7 +220,7 @@ static int __init ic_open_devs(void)
        for_each_netdev(&init_net, dev) {
                if (!(dev->flags & IFF_LOOPBACK) && !netdev_uses_dsa(dev))
                        continue;
-               if (dev_change_flags(dev, dev->flags | IFF_UP) < 0)
+               if (dev_change_flags(dev, dev->flags | IFF_UP, NULL) < 0)
                        pr_err("IP-Config: Failed to open %s\n", dev->name);
        }
 
@@ -238,7 +238,7 @@ static int __init ic_open_devs(void)
                        if (ic_proto_enabled && !able)
                                continue;
                        oflags = dev->flags;
-                       if (dev_change_flags(dev, oflags | IFF_UP) < 0) {
+                       if (dev_change_flags(dev, oflags | IFF_UP, NULL) < 0) {
                                pr_err("IP-Config: Failed to open %s\n",
                                       dev->name);
                                continue;
@@ -315,7 +315,7 @@ static void __init ic_close_devs(void)
                dev = d->dev;
                if (d != ic_dev && !netdev_uses_dsa(dev)) {
                        pr_debug("IP-Config: Downing %s\n", dev->name);
-                       dev_change_flags(dev, d->flags);
+                       dev_change_flags(dev, d->flags, NULL);
                }
                kfree(d);
        }
index e65287c..57c5dd2 100644 (file)
@@ -140,6 +140,13 @@ static int ipip_err(struct sk_buff *skb, u32 info)
        struct ip_tunnel *t;
        int err = 0;
 
+       t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+                            iph->daddr, iph->saddr, 0);
+       if (!t) {
+               err = -ENOENT;
+               goto out;
+       }
+
        switch (type) {
        case ICMP_DEST_UNREACH:
                switch (code) {
@@ -167,13 +174,6 @@ static int ipip_err(struct sk_buff *skb, u32 info)
                goto out;
        }
 
-       t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
-                            iph->daddr, iph->saddr, 0);
-       if (!t) {
-               err = -ENOENT;
-               goto out;
-       }
-
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, net, info, t->parms.link, iph->protocol);
                goto out;
index a6defbe..ea04e38 100644 (file)
@@ -506,7 +506,7 @@ static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
                        dev->flags |= IFF_MULTICAST;
                        if (!ipmr_init_vif_indev(dev))
                                goto failure;
-                       if (dev_open(dev))
+                       if (dev_open(dev, NULL))
                                goto failure;
                        dev_hold(dev);
                }
@@ -589,7 +589,7 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
 
        if (!ipmr_init_vif_indev(dev))
                goto failure;
-       if (dev_open(dev))
+       if (dev_open(dev, NULL))
                goto failure;
 
        dev_hold(dev);
@@ -1802,7 +1802,7 @@ static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
        struct vif_device *out_vif = &mrt->vif_table[out_vifi];
        struct vif_device *in_vif = &mrt->vif_table[in_vifi];
 
-       if (!skb->offload_mr_fwd_mark)
+       if (!skb->offload_l3_fwd_mark)
                return false;
        if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len)
                return false;
index 6d218f5..ca9a5fe 100644 (file)
@@ -6,7 +6,8 @@
 #include <net/tcp.h>
 
 static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
-                             int fc_mx_len, u32 *metrics)
+                             int fc_mx_len, u32 *metrics,
+                             struct netlink_ext_ack *extack)
 {
        bool ecn_ca = false;
        struct nlattr *nla;
@@ -21,19 +22,26 @@ static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
 
                if (!type)
                        continue;
-               if (type > RTAX_MAX)
+               if (type > RTAX_MAX) {
+                       NL_SET_ERR_MSG(extack, "Invalid metric type");
                        return -EINVAL;
+               }
 
                if (type == RTAX_CC_ALGO) {
                        char tmp[TCP_CA_NAME_MAX];
 
                        nla_strlcpy(tmp, nla, sizeof(tmp));
                        val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca);
-                       if (val == TCP_CA_UNSPEC)
+                       if (val == TCP_CA_UNSPEC) {
+                               NL_SET_ERR_MSG(extack, "Unknown tcp congestion algorithm");
                                return -EINVAL;
+                       }
                } else {
-                       if (nla_len(nla) != sizeof(u32))
+                       if (nla_len(nla) != sizeof(u32)) {
+                               NL_SET_ERR_MSG_ATTR(extack, nla,
+                                                   "Invalid attribute in metrics");
                                return -EINVAL;
+                       }
                        val = nla_get_u32(nla);
                }
                if (type == RTAX_ADVMSS && val > 65535 - 40)
@@ -42,8 +50,10 @@ static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
                        val = 65535 - 15;
                if (type == RTAX_HOPLIMIT && val > 255)
                        val = 255;
-               if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK))
+               if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) {
+                       NL_SET_ERR_MSG(extack, "Unknown flag set in feature mask in metrics attribute");
                        return -EINVAL;
+               }
                metrics[type - 1] = val;
        }
 
@@ -54,7 +64,8 @@ static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
 }
 
 struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
-                                       int fc_mx_len)
+                                       int fc_mx_len,
+                                       struct netlink_ext_ack *extack)
 {
        struct dst_metrics *fib_metrics;
        int err;
@@ -66,7 +77,8 @@ struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
        if (unlikely(!fib_metrics))
                return ERR_PTR(-ENOMEM);
 
-       err = ip_metrics_convert(net, fc_mx, fc_mx_len, fib_metrics->metrics);
+       err = ip_metrics_convert(net, fc_mx, fc_mx_len, fib_metrics->metrics,
+                                extack);
        if (!err) {
                refcount_set(&fib_metrics->refcnt, 1);
        } else {
index 7028968..c3610b3 100644 (file)
@@ -219,6 +219,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL),
        SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL),
        SNMP_MIB_ITEM("TCPRcvCollapsed", LINUX_MIB_TCPRCVCOLLAPSED),
+       SNMP_MIB_ITEM("TCPBacklogCoalesce", LINUX_MIB_TCPBACKLOGCOALESCE),
        SNMP_MIB_ITEM("TCPDSACKOldSent", LINUX_MIB_TCPDSACKOLDSENT),
        SNMP_MIB_ITEM("TCPDSACKOfoSent", LINUX_MIB_TCPDSACKOFOSENT),
        SNMP_MIB_ITEM("TCPDSACKRecv", LINUX_MIB_TCPDSACKRECV),
index 32a691b..92d249e 100644 (file)
@@ -29,6 +29,7 @@
 #include <net/protocol.h>
 
 struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
+EXPORT_SYMBOL(inet_protos);
 const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
 EXPORT_SYMBOL(inet_offloads);
 
index 8ca3eb0..076f516 100644 (file)
@@ -131,8 +131,7 @@ struct sock *__raw_v4_lookup(struct net *net, struct sock *sk,
                if (net_eq(sock_net(sk), net) && inet->inet_num == num  &&
                    !(inet->inet_daddr && inet->inet_daddr != raddr)    &&
                    !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
-                   !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif &&
-                     sk->sk_bound_dev_if != sdif))
+                   raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
                        goto found; /* gotcha */
        }
        sk = NULL;
@@ -805,7 +804,7 @@ out:
        return copied;
 }
 
-static int raw_init(struct sock *sk)
+static int raw_sk_init(struct sock *sk)
 {
        struct raw_sock *rp = raw_sk(sk);
 
@@ -970,7 +969,7 @@ struct proto raw_prot = {
        .connect           = ip4_datagram_connect,
        .disconnect        = __udp_disconnect,
        .ioctl             = raw_ioctl,
-       .init              = raw_init,
+       .init              = raw_sk_init,
        .setsockopt        = raw_setsockopt,
        .getsockopt        = raw_getsockopt,
        .sendmsg           = raw_sendmsg,
@@ -1134,3 +1133,27 @@ void __init raw_proc_exit(void)
        unregister_pernet_subsys(&raw_net_ops);
 }
 #endif /* CONFIG_PROC_FS */
+
+static void raw_sysctl_init_net(struct net *net)
+{
+#ifdef CONFIG_NET_L3_MASTER_DEV
+       net->ipv4.sysctl_raw_l3mdev_accept = 1;
+#endif
+}
+
+static int __net_init raw_sysctl_init(struct net *net)
+{
+       raw_sysctl_init_net(net);
+       return 0;
+}
+
+static struct pernet_operations __net_initdata raw_sysctl_ops = {
+       .init   = raw_sysctl_init,
+};
+
+void __init raw_init(void)
+{
+       raw_sysctl_init_net(&init_net);
+       if (register_pernet_subsys(&raw_sysctl_ops))
+               panic("RAW: failed to init sysctl parameters.\n");
+}
index c0a9d26..c4ddbc5 100644 (file)
@@ -1677,7 +1677,7 @@ static void ip_handle_martian_source(struct net_device *dev,
                        print_hex_dump(KERN_WARNING, "ll header: ",
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb_mac_header(skb),
-                                      dev->hard_header_len, true);
+                                      dev->hard_header_len, false);
                }
        }
 #endif
index 891ed2f..ba0fc4b 100644 (file)
@@ -602,6 +602,17 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = ipv4_ping_group_range,
        },
+#ifdef CONFIG_NET_L3_MASTER_DEV
+       {
+               .procname       = "raw_l3mdev_accept",
+               .data           = &init_net.ipv4.sysctl_raw_l3mdev_accept,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
+#endif
        {
                .procname       = "tcp_ecn",
                .data           = &init_net.ipv4.sysctl_tcp_ecn,
index 9e6bc4d..27e2f68 100644 (file)
@@ -1423,7 +1423,7 @@ do_error:
        if (copied + copied_syn)
                goto out;
 out_err:
-       sock_zerocopy_put_abort(uarg);
+       sock_zerocopy_put_abort(uarg, true);
        err = sk_stream_error(sk, flags, err);
        /* make sure we wake any epoll edge trigger waiter */
        if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 &&
@@ -2088,7 +2088,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
                }
                continue;
 
-       found_ok_skb:
+found_ok_skb:
                /* Ok so how much can we use? */
                used = skb->len - offset;
                if (len < used)
@@ -2147,7 +2147,7 @@ skip_copy:
                        sk_eat_skb(sk, skb);
                continue;
 
-       found_fin_ok:
+found_fin_ok:
                /* Process the FIN. */
                ++*seq;
                if (!(flags & MSG_PEEK))
@@ -2241,10 +2241,6 @@ void tcp_set_state(struct sock *sk, int state)
         * socket sitting in hash tables.
         */
        inet_sk_state_store(sk, state);
-
-#ifdef STATE_TRACE
-       SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n", sk, statename[oldstate], statename[state]);
-#endif
 }
 EXPORT_SYMBOL_GPL(tcp_set_state);
 
@@ -3246,6 +3242,7 @@ static size_t tcp_opt_stats_get_size(void)
                nla_total_size_64bit(sizeof(u64)) + /* TCP_NLA_BYTES_RETRANS */
                nla_total_size(sizeof(u32)) + /* TCP_NLA_DSACK_DUPS */
                nla_total_size(sizeof(u32)) + /* TCP_NLA_REORD_SEEN */
+               nla_total_size(sizeof(u32)) + /* TCP_NLA_SRTT */
                0;
 }
 
@@ -3299,6 +3296,7 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk)
                          TCP_NLA_PAD);
        nla_put_u32(stats, TCP_NLA_DSACK_DUPS, tp->dsack_dups);
        nla_put_u32(stats, TCP_NLA_REORD_SEEN, tp->reord_seen);
+       nla_put_u32(stats, TCP_NLA_SRTT, tp->srtt_us >> 3);
 
        return stats;
 }
@@ -3658,8 +3656,11 @@ bool tcp_alloc_md5sig_pool(void)
        if (unlikely(!tcp_md5sig_pool_populated)) {
                mutex_lock(&tcp_md5sig_mutex);
 
-               if (!tcp_md5sig_pool_populated)
+               if (!tcp_md5sig_pool_populated) {
                        __tcp_alloc_md5sig_pool();
+                       if (tcp_md5sig_pool_populated)
+                               static_key_slow_inc(&tcp_md5_needed);
+               }
 
                mutex_unlock(&tcp_md5sig_mutex);
        }
index 9277abd..0f497fc 100644 (file)
@@ -128,7 +128,12 @@ static const u32 bbr_probe_rtt_mode_ms = 200;
 /* Skip TSO below the following bandwidth (bits/sec): */
 static const int bbr_min_tso_rate = 1200000;
 
-/* Pace at ~1% below estimated bw, on average, to reduce queue at bottleneck. */
+/* Pace at ~1% below estimated bw, on average, to reduce queue at bottleneck.
+ * In order to help drive the network toward lower queues and low latency while
+ * maintaining high utilization, the average pacing rate aims to be slightly
+ * lower than the estimated bandwidth. This is an important aspect of the
+ * design.
+ */
 static const int bbr_pacing_margin_percent = 1;
 
 /* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain
@@ -247,13 +252,7 @@ static void bbr_init_pacing_rate_from_rtt(struct sock *sk)
        sk->sk_pacing_rate = bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain);
 }
 
-/* Pace using current bw estimate and a gain factor. In order to help drive the
- * network toward lower queues while maintaining high utilization and low
- * latency, the average pacing rate aims to be slightly (~1%) lower than the
- * estimated bandwidth. This is an important aspect of the design. In this
- * implementation this slightly lower pacing rate is achieved implicitly by not
- * including link-layer headers in the packet size used for the pacing rate.
- */
+/* Pace using current bw estimate and a gain factor. */
 static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
 {
        struct tcp_sock *tp = tcp_sk(sk);
index 3b45fe5..a47c1cd 100644 (file)
@@ -289,12 +289,23 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock,
 {
        bool cork = false, enospc = msg->sg.start == msg->sg.end;
        struct sock *sk_redir;
-       u32 tosend;
+       u32 tosend, delta = 0;
        int ret;
 
 more_data:
-       if (psock->eval == __SK_NONE)
+       if (psock->eval == __SK_NONE) {
+               /* Track delta in msg size to add/subtract it on SK_DROP from
+                * returned to user copied size. This ensures user doesn't
+                * get a positive return code with msg_cut_data and SK_DROP
+                * verdict.
+                */
+               delta = msg->sg.size;
                psock->eval = sk_psock_msg_verdict(sk, psock, msg);
+               if (msg->sg.size < delta)
+                       delta -= msg->sg.size;
+               else
+                       delta = 0;
+       }
 
        if (msg->cork_bytes &&
            msg->cork_bytes > msg->sg.size && !enospc) {
@@ -350,7 +361,7 @@ more_data:
        default:
                sk_msg_free_partial(sk, msg, tosend);
                sk_msg_apply_bytes(psock, tosend);
-               *copied -= tosend;
+               *copied -= (tosend + delta);
                return -EACCES;
        }
 
index a9d9555..76858b1 100644 (file)
@@ -1865,16 +1865,20 @@ static void tcp_check_reno_reordering(struct sock *sk, const int addend)
 
 /* Emulate SACKs for SACKless connection: account for a new dupack. */
 
-static void tcp_add_reno_sack(struct sock *sk)
+static void tcp_add_reno_sack(struct sock *sk, int num_dupack)
 {
-       struct tcp_sock *tp = tcp_sk(sk);
-       u32 prior_sacked = tp->sacked_out;
+       if (num_dupack) {
+               struct tcp_sock *tp = tcp_sk(sk);
+               u32 prior_sacked = tp->sacked_out;
+               s32 delivered;
 
-       tp->sacked_out++;
-       tcp_check_reno_reordering(sk, 0);
-       if (tp->sacked_out > prior_sacked)
-               tp->delivered++; /* Some out-of-order packet is delivered */
-       tcp_verify_left_out(tp);
+               tp->sacked_out += num_dupack;
+               tcp_check_reno_reordering(sk, 0);
+               delivered = tp->sacked_out - prior_sacked;
+               if (delivered > 0)
+                       tp->delivered += delivered;
+               tcp_verify_left_out(tp);
+       }
 }
 
 /* Account for ACK, ACKing some data in Reno Recovery phase. */
@@ -2459,8 +2463,8 @@ void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, int flag)
                u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered +
                               tp->prior_cwnd - 1;
                sndcnt = div_u64(dividend, tp->prior_cwnd) - tp->prr_out;
-       } else if ((flag & FLAG_RETRANS_DATA_ACKED) &&
-                  !(flag & FLAG_LOST_RETRANS)) {
+       } else if ((flag & (FLAG_RETRANS_DATA_ACKED | FLAG_LOST_RETRANS)) ==
+                  FLAG_RETRANS_DATA_ACKED) {
                sndcnt = min_t(int, delta,
                               max_t(int, tp->prr_delivered - tp->prr_out,
                                     newly_acked_sacked) + 1);
@@ -2636,7 +2640,7 @@ void tcp_enter_recovery(struct sock *sk, bool ece_ack)
 /* 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,
+static void tcp_process_loss(struct sock *sk, int flag, int num_dupack,
                             int *rexmit)
 {
        struct tcp_sock *tp = tcp_sk(sk);
@@ -2655,7 +2659,7 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
                        return;
 
                if (after(tp->snd_nxt, tp->high_seq)) {
-                       if (flag & FLAG_DATA_SACKED || is_dupack)
+                       if (flag & FLAG_DATA_SACKED || num_dupack)
                                tp->frto = 0; /* Step 3.a. loss was real */
                } else if (flag & FLAG_SND_UNA_ADVANCED && !recovered) {
                        tp->high_seq = tp->snd_nxt;
@@ -2681,8 +2685,8 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack,
                /* 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);
+               if (after(tp->snd_nxt, tp->high_seq) && num_dupack)
+                       tcp_add_reno_sack(sk, num_dupack);
                else if (flag & FLAG_SND_UNA_ADVANCED)
                        tcp_reset_reno_sack(tp);
        }
@@ -2759,13 +2763,13 @@ static bool tcp_force_fast_retransmit(struct sock *sk)
  * tcp_xmit_retransmit_queue().
  */
 static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
-                                 bool is_dupack, int *ack_flag, int *rexmit)
+                                 int num_dupack, int *ack_flag, int *rexmit)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        int fast_rexmit = 0, flag = *ack_flag;
-       bool do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
-                                    tcp_force_fast_retransmit(sk));
+       bool do_lost = num_dupack || ((flag & FLAG_DATA_SACKED) &&
+                                     tcp_force_fast_retransmit(sk));
 
        if (!tp->packets_out && tp->sacked_out)
                tp->sacked_out = 0;
@@ -2812,8 +2816,8 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
        switch (icsk->icsk_ca_state) {
        case TCP_CA_Recovery:
                if (!(flag & FLAG_SND_UNA_ADVANCED)) {
-                       if (tcp_is_reno(tp) && is_dupack)
-                               tcp_add_reno_sack(sk);
+                       if (tcp_is_reno(tp))
+                               tcp_add_reno_sack(sk, num_dupack);
                } else {
                        if (tcp_try_undo_partial(sk, prior_snd_una))
                                return;
@@ -2828,7 +2832,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                tcp_identify_packet_loss(sk, ack_flag);
                break;
        case TCP_CA_Loss:
-               tcp_process_loss(sk, flag, is_dupack, rexmit);
+               tcp_process_loss(sk, flag, num_dupack, rexmit);
                tcp_identify_packet_loss(sk, ack_flag);
                if (!(icsk->icsk_ca_state == TCP_CA_Open ||
                      (*ack_flag & FLAG_LOST_RETRANS)))
@@ -2839,8 +2843,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                if (tcp_is_reno(tp)) {
                        if (flag & FLAG_SND_UNA_ADVANCED)
                                tcp_reset_reno_sack(tp);
-                       if (is_dupack)
-                               tcp_add_reno_sack(sk);
+                       tcp_add_reno_sack(sk, num_dupack);
                }
 
                if (icsk->icsk_ca_state <= TCP_CA_Disorder)
@@ -3562,7 +3565,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        bool is_sack_reneg = tp->is_sack_reneg;
        u32 ack_seq = TCP_SKB_CB(skb)->seq;
        u32 ack = TCP_SKB_CB(skb)->ack_seq;
-       bool is_dupack = false;
+       int num_dupack = 0;
        int prior_packets = tp->packets_out;
        u32 delivered = tp->delivered;
        u32 lost = tp->lost;
@@ -3614,7 +3617,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        if (flag & FLAG_UPDATE_TS_RECENT)
                tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
 
-       if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) {
+       if ((flag & (FLAG_SLOWPATH | FLAG_SND_UNA_ADVANCED)) ==
+           FLAG_SND_UNA_ADVANCED) {
                /* Window is constant, pure forward advance.
                 * No more checks are required.
                 * Note, we use the fact that SND.UNA>=SND.WL2.
@@ -3672,8 +3676,13 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                tcp_set_xmit_timer(sk);
 
        if (tcp_ack_is_dubious(sk, flag)) {
-               is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               if (!(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP))) {
+                       num_dupack = 1;
+                       /* Consider if pure acks were aggregated in tcp_add_backlog() */
+                       if (!(flag & FLAG_DATA))
+                               num_dupack = max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+               }
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
        }
 
@@ -3691,7 +3700,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 no_queue:
        /* If data was DSACKed, see if we can undo a cwnd reduction. */
        if (flag & FLAG_DSACKING_ACK) {
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
                tcp_newly_delivered(sk, delivered, flag);
        }
@@ -3716,7 +3725,7 @@ old_ack:
        if (TCP_SKB_CB(skb)->sacked) {
                flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
                                                &sack_state);
-               tcp_fastretrans_alert(sk, prior_snd_una, is_dupack, &flag,
+               tcp_fastretrans_alert(sk, prior_snd_una, num_dupack, &flag,
                                      &rexmit);
                tcp_newly_delivered(sk, delivered, flag);
                tcp_xmit_recovery(sk, rexmit);
@@ -4606,13 +4615,12 @@ end:
        }
 }
 
-static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
-                 bool *fragstolen)
+static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb,
+                                     bool *fragstolen)
 {
        int eaten;
        struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
 
-       __skb_pull(skb, hdrlen);
        eaten = (tail &&
                 tcp_try_coalesce(sk, tail,
                                  skb, fragstolen)) ? 1 : 0;
@@ -4663,7 +4671,7 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
        TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + size;
        TCP_SKB_CB(skb)->ack_seq = tcp_sk(sk)->snd_una - 1;
 
-       if (tcp_queue_rcv(sk, skb, 0, &fragstolen)) {
+       if (tcp_queue_rcv(sk, skb, &fragstolen)) {
                WARN_ON_ONCE(fragstolen); /* should not happen */
                __kfree_skb(skb);
        }
@@ -4723,7 +4731,7 @@ queue_and_out:
                        goto drop;
                }
 
-               eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
+               eaten = tcp_queue_rcv(sk, skb, &fragstolen);
                if (skb->len)
                        tcp_event_data_recv(sk, skb);
                if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
@@ -5599,8 +5607,8 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
                        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS);
 
                        /* Bulk data transfer: receiver */
-                       eaten = tcp_queue_rcv(sk, skb, tcp_header_len,
-                                             &fragstolen);
+                       __skb_pull(skb, tcp_header_len);
+                       eaten = tcp_queue_rcv(sk, skb, &fragstolen);
 
                        tcp_event_data_recv(sk, skb);
 
index de47038..efc6fef 100644 (file)
@@ -423,7 +423,7 @@ EXPORT_SYMBOL(tcp_req_err);
  *
  */
 
-void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
+int tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
 {
        const struct iphdr *iph = (const struct iphdr *)icmp_skb->data;
        struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2));
@@ -446,20 +446,21 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                                       inet_iif(icmp_skb), 0);
        if (!sk) {
                __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
-               return;
+               return -ENOENT;
        }
        if (sk->sk_state == TCP_TIME_WAIT) {
                inet_twsk_put(inet_twsk(sk));
-               return;
+               return 0;
        }
        seq = ntohl(th->seq);
-       if (sk->sk_state == TCP_NEW_SYN_RECV)
-               return tcp_req_err(sk, seq,
-                                 type == ICMP_PARAMETERPROB ||
-                                 type == ICMP_TIME_EXCEEDED ||
-                                 (type == ICMP_DEST_UNREACH &&
-                                  (code == ICMP_NET_UNREACH ||
-                                   code == ICMP_HOST_UNREACH)));
+       if (sk->sk_state == TCP_NEW_SYN_RECV) {
+               tcp_req_err(sk, seq, type == ICMP_PARAMETERPROB ||
+                                    type == ICMP_TIME_EXCEEDED ||
+                                    (type == ICMP_DEST_UNREACH &&
+                                     (code == ICMP_NET_UNREACH ||
+                                      code == ICMP_HOST_UNREACH)));
+               return 0;
+       }
 
        bh_lock_sock(sk);
        /* If too many ICMPs get dropped on busy
@@ -541,7 +542,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                icsk->icsk_rto = inet_csk_rto_backoff(icsk, TCP_RTO_MAX);
 
                skb = tcp_rtx_queue_head(sk);
-               BUG_ON(!skb);
 
                tcp_mstamp_refresh(tp);
                delta_us = (u32)(tp->tcp_mstamp - tcp_skb_timestamp_us(skb));
@@ -613,6 +613,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
+       return 0;
 }
 
 void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
@@ -969,10 +970,13 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
  * We need to maintain these in the sk structure.
  */
 
+struct static_key tcp_md5_needed __read_mostly;
+EXPORT_SYMBOL(tcp_md5_needed);
+
 /* Find the Key structure for an address.  */
-struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
-                                        const union tcp_md5_addr *addr,
-                                        int family)
+struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk,
+                                          const union tcp_md5_addr *addr,
+                                          int family)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
@@ -1010,7 +1014,7 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(const struct sock *sk,
        }
        return best_match;
 }
-EXPORT_SYMBOL(tcp_md5_do_lookup);
+EXPORT_SYMBOL(__tcp_md5_do_lookup);
 
 static struct tcp_md5sig_key *tcp_md5_do_lookup_exact(const struct sock *sk,
                                                      const union tcp_md5_addr *addr,
@@ -1618,12 +1622,14 @@ int tcp_v4_early_demux(struct sk_buff *skb)
 bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
 {
        u32 limit = sk->sk_rcvbuf + sk->sk_sndbuf;
-
-       /* Only socket owner can try to collapse/prune rx queues
-        * to reduce memory overhead, so add a little headroom here.
-        * Few sockets backlog are possibly concurrently non empty.
-        */
-       limit += 64*1024;
+       struct skb_shared_info *shinfo;
+       const struct tcphdr *th;
+       struct tcphdr *thtail;
+       struct sk_buff *tail;
+       unsigned int hdrlen;
+       bool fragstolen;
+       u32 gso_segs;
+       int delta;
 
        /* In case all data was pulled from skb frags (in __pskb_pull_tail()),
         * we can fix skb->truesize to its real value to avoid future drops.
@@ -1633,6 +1639,86 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
         */
        skb_condense(skb);
 
+       skb_dst_drop(skb);
+
+       if (unlikely(tcp_checksum_complete(skb))) {
+               bh_unlock_sock(sk);
+               __TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
+               __TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
+               return true;
+       }
+
+       /* Attempt coalescing to last skb in backlog, even if we are
+        * above the limits.
+        * This is okay because skb capacity is limited to MAX_SKB_FRAGS.
+        */
+       th = (const struct tcphdr *)skb->data;
+       hdrlen = th->doff * 4;
+       shinfo = skb_shinfo(skb);
+
+       if (!shinfo->gso_size)
+               shinfo->gso_size = skb->len - hdrlen;
+
+       if (!shinfo->gso_segs)
+               shinfo->gso_segs = 1;
+
+       tail = sk->sk_backlog.tail;
+       if (!tail)
+               goto no_coalesce;
+       thtail = (struct tcphdr *)tail->data;
+
+       if (TCP_SKB_CB(tail)->end_seq != TCP_SKB_CB(skb)->seq ||
+           TCP_SKB_CB(tail)->ip_dsfield != TCP_SKB_CB(skb)->ip_dsfield ||
+           ((TCP_SKB_CB(tail)->tcp_flags |
+             TCP_SKB_CB(skb)->tcp_flags) & TCPHDR_URG) ||
+           ((TCP_SKB_CB(tail)->tcp_flags ^
+             TCP_SKB_CB(skb)->tcp_flags) & (TCPHDR_ECE | TCPHDR_CWR)) ||
+#ifdef CONFIG_TLS_DEVICE
+           tail->decrypted != skb->decrypted ||
+#endif
+           thtail->doff != th->doff ||
+           memcmp(thtail + 1, th + 1, hdrlen - sizeof(*th)))
+               goto no_coalesce;
+
+       __skb_pull(skb, hdrlen);
+       if (skb_try_coalesce(tail, skb, &fragstolen, &delta)) {
+               thtail->window = th->window;
+
+               TCP_SKB_CB(tail)->end_seq = TCP_SKB_CB(skb)->end_seq;
+
+               if (after(TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(tail)->ack_seq))
+                       TCP_SKB_CB(tail)->ack_seq = TCP_SKB_CB(skb)->ack_seq;
+
+               TCP_SKB_CB(tail)->tcp_flags |= TCP_SKB_CB(skb)->tcp_flags;
+
+               if (TCP_SKB_CB(skb)->has_rxtstamp) {
+                       TCP_SKB_CB(tail)->has_rxtstamp = true;
+                       tail->tstamp = skb->tstamp;
+                       skb_hwtstamps(tail)->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
+               }
+
+               /* Not as strict as GRO. We only need to carry mss max value */
+               skb_shinfo(tail)->gso_size = max(shinfo->gso_size,
+                                                skb_shinfo(tail)->gso_size);
+
+               gso_segs = skb_shinfo(tail)->gso_segs + shinfo->gso_segs;
+               skb_shinfo(tail)->gso_segs = min_t(u32, gso_segs, 0xFFFF);
+
+               sk->sk_backlog.len += delta;
+               __NET_INC_STATS(sock_net(sk),
+                               LINUX_MIB_TCPBACKLOGCOALESCE);
+               kfree_skb_partial(skb, fragstolen);
+               return false;
+       }
+       __skb_push(skb, hdrlen);
+
+no_coalesce:
+       /* Only socket owner can try to collapse/prune rx queues
+        * to reduce memory overhead, so add a little headroom here.
+        * Few sockets backlog are possibly concurrently non empty.
+        */
+       limit += 64*1024;
+
        if (unlikely(sk_add_backlog(sk, skb, limit))) {
                bh_unlock_sock(sk);
                __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPBACKLOGDROP);
@@ -2573,8 +2659,8 @@ static int __net_init tcp_sk_init(struct net *net)
         * which are too large can cause TCP streams to be bursty.
         */
        net->ipv4.sysctl_tcp_tso_win_divisor = 3;
-       /* Default TSQ limit of four TSO segments */
-       net->ipv4.sysctl_tcp_limit_output_bytes = 262144;
+       /* Default TSQ limit of 16 TSO segments */
+       net->ipv4.sysctl_tcp_limit_output_bytes = 16 * 65536;
        /* rfc5961 challenge ack rate limiting */
        net->ipv4.sysctl_tcp_challenge_ack_limit = 1000;
        net->ipv4.sysctl_tcp_min_tso_segs = 2;
index d1676d8..c31badf 100644 (file)
@@ -233,16 +233,14 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
        if (init_rcv_wnd)
                *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
 
-       (*rcv_wscale) = 0;
+       *rcv_wscale = 0;
        if (wscale_ok) {
                /* Set window scaling on max possible window */
                space = max_t(u32, space, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
                space = max_t(u32, space, sysctl_rmem_max);
                space = min_t(u32, space, *window_clamp);
-               while (space > U16_MAX && (*rcv_wscale) < TCP_MAX_WSCALE) {
-                       space >>= 1;
-                       (*rcv_wscale)++;
-               }
+               *rcv_wscale = clamp_t(int, ilog2(space) - 15,
+                                     0, TCP_MAX_WSCALE);
        }
        /* Set the clamp no higher than max representable value */
        (*window_clamp) = min_t(__u32, U16_MAX << (*rcv_wscale), *window_clamp);
@@ -596,7 +594,8 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 
        *md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-       if (unlikely(rcu_access_pointer(tp->md5sig_info))) {
+       if (static_key_false(&tcp_md5_needed) &&
+           rcu_access_pointer(tp->md5sig_info)) {
                *md5 = tp->af_specific->md5_lookup(sk, sk);
                if (*md5) {
                        opts->options |= OPTION_MD5;
@@ -732,7 +731,8 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
 
        *md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-       if (unlikely(rcu_access_pointer(tp->md5sig_info))) {
+       if (static_key_false(&tcp_md5_needed) &&
+           rcu_access_pointer(tp->md5sig_info)) {
                *md5 = tp->af_specific->md5_lookup(sk, sk);
                if (*md5) {
                        opts->options |= OPTION_MD5;
@@ -1909,18 +1909,22 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
                                 u32 max_segs)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       u32 age, send_win, cong_win, limit, in_flight;
+       u32 send_win, cong_win, limit, in_flight;
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *head;
        int win_divisor;
+       s64 delta;
 
        if (icsk->icsk_ca_state >= TCP_CA_Recovery)
                goto send_now;
 
        /* Avoid bursty behavior by allowing defer
-        * only if the last write was recent.
+        * only if the last write was recent (1 ms).
+        * Note that tp->tcp_wstamp_ns can be in the future if we have
+        * packets waiting in a qdisc or device for EDT delivery.
         */
-       if ((s32)(tcp_jiffies32 - tp->lsndtime) > 0)
+       delta = tp->tcp_clock_cache - tp->tcp_wstamp_ns - NSEC_PER_MSEC;
+       if (delta > 0)
                goto send_now;
 
        in_flight = tcp_packets_in_flight(tp);
@@ -1943,6 +1947,10 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len))
                goto send_now;
 
+       /* If this packet won't get more data, do not wait. */
+       if (TCP_SKB_CB(skb)->eor)
+               goto send_now;
+
        win_divisor = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_win_divisor);
        if (win_divisor) {
                u32 chunk = min(tp->snd_wnd, tp->snd_cwnd * tp->mss_cache);
@@ -1967,9 +1975,9 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
        head = tcp_rtx_queue_head(sk);
        if (!head)
                goto send_now;
-       age = tcp_stamp_us_delta(tp->tcp_mstamp, tcp_skb_timestamp_us(head));
+       delta = tp->tcp_clock_cache - head->tstamp;
        /* If next ACK is likely to come too late (half srtt), do not defer */
-       if (age < (tp->srtt_us >> 4))
+       if ((s64)(delta - (u64)NSEC_PER_USEC * (tp->srtt_us >> 4)) < 0)
                goto send_now;
 
        /* Ok, it looks like it is advisable to defer.
@@ -2228,8 +2236,9 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb,
        limit = max_t(unsigned long,
                      2 * skb->truesize,
                      sk->sk_pacing_rate >> sk->sk_pacing_shift);
-       limit = min_t(unsigned long, limit,
-                     sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes);
+       if (sk->sk_pacing_status == SK_PACING_NONE)
+               limit = min_t(unsigned long, limit,
+                             sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes);
        limit <<= factor;
 
        if (refcount_read(&sk->sk_wmem_alloc) > limit) {
index c063001..33bf8e9 100644 (file)
@@ -149,34 +149,40 @@ drop:
 }
 #endif
 
-static void tunnel4_err(struct sk_buff *skb, u32 info)
+static int tunnel4_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm_tunnel *handler;
 
        for_each_tunnel_rcu(tunnel4_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static void tunnel64_err(struct sk_buff *skb, u32 info)
+static int tunnel64_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm_tunnel *handler;
 
        for_each_tunnel_rcu(tunnel64_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 #endif
 
 #if IS_ENABLED(CONFIG_MPLS)
-static void tunnelmpls4_err(struct sk_buff *skb, u32 info)
+static int tunnelmpls4_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm_tunnel *handler;
 
        for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 #endif
 
index 1976fdd..aff2a8e 100644 (file)
 #include <net/net_namespace.h>
 #include <net/icmp.h>
 #include <net/inet_hashtables.h>
+#include <net/ip_tunnels.h>
 #include <net/route.h>
 #include <net/checksum.h>
 #include <net/xfrm.h>
 #include "udp_impl.h"
 #include <net/sock_reuseport.h>
 #include <net/addrconf.h>
+#include <net/udp_tunnel.h>
 
 struct udp_table udp_table __read_mostly;
 EXPORT_SYMBOL(udp_table);
@@ -371,6 +373,7 @@ static int compute_score(struct sock *sk, struct net *net,
 {
        int score;
        struct inet_sock *inet;
+       bool dev_match;
 
        if (!net_eq(sock_net(sk), net) ||
            udp_sk(sk)->udp_port_hash != hnum ||
@@ -398,15 +401,11 @@ static int compute_score(struct sock *sk, struct net *net,
                score += 4;
        }
 
-       if (sk->sk_bound_dev_if || exact_dif) {
-               bool dev_match = (sk->sk_bound_dev_if == dif ||
-                                 sk->sk_bound_dev_if == sdif);
-
-               if (!dev_match)
-                       return -1;
-               if (sk->sk_bound_dev_if)
-                       score += 4;
-       }
+       dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+                                       dif, sdif);
+       if (!dev_match)
+               return -1;
+       score += 4;
 
        if (sk->sk_incoming_cpu == raw_smp_processor_id())
                score++;
@@ -585,6 +584,89 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
        return true;
 }
 
+DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
+void udp_encap_enable(void)
+{
+       static_branch_inc(&udp_encap_needed_key);
+}
+EXPORT_SYMBOL(udp_encap_enable);
+
+/* Handler for tunnels with arbitrary destination ports: no socket lookup, go
+ * through error handlers in encapsulations looking for a match.
+ */
+static int __udp4_lib_err_encap_no_sk(struct sk_buff *skb, u32 info)
+{
+       int i;
+
+       for (i = 0; i < MAX_IPTUN_ENCAP_OPS; i++) {
+               int (*handler)(struct sk_buff *skb, u32 info);
+
+               if (!iptun_encaps[i])
+                       continue;
+               handler = rcu_dereference(iptun_encaps[i]->err_handler);
+               if (handler && !handler(skb, info))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+/* Try to match ICMP errors to UDP tunnels by looking up a socket without
+ * reversing source and destination port: this will match tunnels that force the
+ * same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that
+ * lwtunnels might actually break this assumption by being configured with
+ * different destination ports on endpoints, in this case we won't be able to
+ * trace ICMP messages back to them.
+ *
+ * If this doesn't match any socket, probe tunnels with arbitrary destination
+ * ports (e.g. FoU, GUE): there, the receiving socket is useless, as the port
+ * we've sent packets to won't necessarily match the local destination port.
+ *
+ * Then ask the tunnel implementation to match the error against a valid
+ * association.
+ *
+ * Return an error if we can't find a match, the socket if we need further
+ * processing, zero otherwise.
+ */
+static struct sock *__udp4_lib_err_encap(struct net *net,
+                                        const struct iphdr *iph,
+                                        struct udphdr *uh,
+                                        struct udp_table *udptable,
+                                        struct sk_buff *skb, u32 info)
+{
+       int network_offset, transport_offset;
+       struct sock *sk;
+
+       network_offset = skb_network_offset(skb);
+       transport_offset = skb_transport_offset(skb);
+
+       /* Network header needs to point to the outer IPv4 header inside ICMP */
+       skb_reset_network_header(skb);
+
+       /* Transport header needs to point to the UDP header */
+       skb_set_transport_header(skb, iph->ihl << 2);
+
+       sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
+                              iph->saddr, uh->dest, skb->dev->ifindex, 0,
+                              udptable, NULL);
+       if (sk) {
+               int (*lookup)(struct sock *sk, struct sk_buff *skb);
+               struct udp_sock *up = udp_sk(sk);
+
+               lookup = READ_ONCE(up->encap_err_lookup);
+               if (!lookup || lookup(sk, skb))
+                       sk = NULL;
+       }
+
+       if (!sk)
+               sk = ERR_PTR(__udp4_lib_err_encap_no_sk(skb, info));
+
+       skb_set_transport_header(skb, transport_offset);
+       skb_set_network_header(skb, network_offset);
+
+       return sk;
+}
+
 /*
  * This routine is called by the ICMP module when it gets some
  * sort of error condition.  If err < 0 then the socket should
@@ -596,13 +678,14 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
  * to find the appropriate port.
  */
 
-void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
+int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
 {
        struct inet_sock *inet;
        const struct iphdr *iph = (const struct iphdr *)skb->data;
        struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
+       bool tunnel = false;
        struct sock *sk;
        int harderr;
        int err;
@@ -612,8 +695,21 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
                               iph->saddr, uh->source, skb->dev->ifindex,
                               inet_sdif(skb), udptable, NULL);
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
-               return; /* No socket for error */
+               /* No socket for error: try tunnels before discarding */
+               sk = ERR_PTR(-ENOENT);
+               if (static_branch_unlikely(&udp_encap_needed_key)) {
+                       sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb,
+                                                 info);
+                       if (!sk)
+                               return 0;
+               }
+
+               if (IS_ERR(sk)) {
+                       __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+                       return PTR_ERR(sk);
+               }
+
+               tunnel = true;
        }
 
        err = 0;
@@ -656,6 +752,10 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
         *      RFC1122: OK.  Passes ICMP errors back to application, as per
         *      4.1.3.3.
         */
+       if (tunnel) {
+               /* ...not for tunnels though: we don't have a sending socket */
+               goto out;
+       }
        if (!inet->recverr) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
@@ -665,12 +765,12 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
        sk->sk_err = err;
        sk->sk_error_report(sk);
 out:
-       return;
+       return 0;
 }
 
-void udp_err(struct sk_buff *skb, u32 info)
+int udp_err(struct sk_buff *skb, u32 info)
 {
-       __udp4_lib_err(skb, info, &udp_table);
+       return __udp4_lib_err(skb, info, &udp_table);
 }
 
 /*
@@ -1713,6 +1813,10 @@ try_again:
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
                *addr_len = sizeof(*sin);
        }
+
+       if (udp_sk(sk)->gro_enabled)
+               udp_cmsg_recv(msg, sk, skb);
+
        if (inet->cmsg_flags)
                ip_cmsg_recv_offset(msg, sk, skb, sizeof(struct udphdr), off);
 
@@ -1889,13 +1993,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
-DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
-void udp_encap_enable(void)
-{
-       static_branch_enable(&udp_encap_needed_key);
-}
-EXPORT_SYMBOL(udp_encap_enable);
-
 /* returns:
  *  -1: error
  *   0: success
@@ -1904,7 +2001,7 @@ EXPORT_SYMBOL(udp_encap_enable);
  * Note that in the success and error cases, the skb is assumed to
  * have either been requeued or freed.
  */
-static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
 {
        struct udp_sock *up = udp_sk(sk);
        int is_udplite = IS_UDPLITE(sk);
@@ -2007,6 +2104,27 @@ drop:
        return -1;
 }
 
+static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       struct sk_buff *next, *segs;
+       int ret;
+
+       if (likely(!udp_unexpected_gso(sk, skb)))
+               return udp_queue_rcv_one_skb(sk, skb);
+
+       BUILD_BUG_ON(sizeof(struct udp_skb_cb) > SKB_SGO_CB_OFFSET);
+       __skb_push(skb, -skb_mac_offset(skb));
+       segs = udp_rcv_segment(sk, skb, true);
+       for (skb = segs; skb; skb = next) {
+               next = skb->next;
+               __skb_pull(skb, skb_transport_offset(skb));
+               ret = udp_queue_rcv_one_skb(sk, skb);
+               if (ret > 0)
+                       ip_protocol_deliver_rcu(dev_net(skb->dev), skb, -ret);
+       }
+       return 0;
+}
+
 /* For TCP sockets, sk_rx_dst is protected by socket lock
  * For UDP, we use xchg() to guard against concurrent changes.
  */
@@ -2398,11 +2516,15 @@ void udp_destroy_sock(struct sock *sk)
        bool slow = lock_sock_fast(sk);
        udp_flush_pending_frames(sk);
        unlock_sock_fast(sk, slow);
-       if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) {
-               void (*encap_destroy)(struct sock *sk);
-               encap_destroy = READ_ONCE(up->encap_destroy);
-               if (encap_destroy)
-                       encap_destroy(sk);
+       if (static_branch_unlikely(&udp_encap_needed_key)) {
+               if (up->encap_type) {
+                       void (*encap_destroy)(struct sock *sk);
+                       encap_destroy = READ_ONCE(up->encap_destroy);
+                       if (encap_destroy)
+                               encap_destroy(sk);
+               }
+               if (up->encap_enabled)
+                       static_branch_dec(&udp_encap_needed_key);
        }
 }
 
@@ -2447,7 +2569,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                        /* FALLTHROUGH */
                case UDP_ENCAP_L2TPINUDP:
                        up->encap_type = val;
-                       udp_encap_enable();
+                       lock_sock(sk);
+                       udp_tunnel_encap_enable(sk->sk_socket);
+                       release_sock(sk);
                        break;
                default:
                        err = -ENOPROTOOPT;
@@ -2469,6 +2593,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                up->gso_size = val;
                break;
 
+       case UDP_GRO:
+               lock_sock(sk);
+               if (valbool)
+                       udp_tunnel_encap_enable(sk->sk_socket);
+               up->gro_enabled = valbool;
+               release_sock(sk);
+               break;
+
        /*
         *      UDP-Lite's partial checksum coverage (RFC 3828).
         */
index e7d18b1..3226726 100644 (file)
@@ -7,7 +7,7 @@
 #include <net/inet_common.h>
 
 int __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int);
-void __udp4_lib_err(struct sk_buff *, u32, struct udp_table *);
+int __udp4_lib_err(struct sk_buff *, u32, struct udp_table *);
 
 int udp_v4_get_port(struct sock *sk, unsigned short snum);
 
index 802f2bc..0646d61 100644 (file)
@@ -343,6 +343,54 @@ out:
        return segs;
 }
 
+#define UDP_GRO_CNT_MAX 64
+static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
+                                              struct sk_buff *skb)
+{
+       struct udphdr *uh = udp_hdr(skb);
+       struct sk_buff *pp = NULL;
+       struct udphdr *uh2;
+       struct sk_buff *p;
+
+       /* requires non zero csum, for symmetry with GSO */
+       if (!uh->check) {
+               NAPI_GRO_CB(skb)->flush = 1;
+               return NULL;
+       }
+
+       /* pull encapsulating udp header */
+       skb_gro_pull(skb, sizeof(struct udphdr));
+       skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
+
+       list_for_each_entry(p, head, list) {
+               if (!NAPI_GRO_CB(p)->same_flow)
+                       continue;
+
+               uh2 = udp_hdr(p);
+
+               /* Match ports only, as csum is always non zero */
+               if ((*(u32 *)&uh->source != *(u32 *)&uh2->source)) {
+                       NAPI_GRO_CB(p)->same_flow = 0;
+                       continue;
+               }
+
+               /* Terminate the flow on len mismatch or if it grow "too much".
+                * Under small packet flood GRO count could elsewhere grow a lot
+                * leading to execessive truesize values
+                */
+               if (!skb_gro_receive(p, skb) &&
+                   NAPI_GRO_CB(p)->count >= UDP_GRO_CNT_MAX)
+                       pp = p;
+               else if (uh->len != uh2->len)
+                       pp = p;
+
+               return pp;
+       }
+
+       /* mismatch, but we never need to flush */
+       return NULL;
+}
+
 struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
                                struct udphdr *uh, udp_lookup_t lookup)
 {
@@ -353,23 +401,27 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
        int flush = 1;
        struct sock *sk;
 
+       rcu_read_lock();
+       sk = (*lookup)(skb, uh->source, uh->dest);
+       if (!sk)
+               goto out_unlock;
+
+       if (udp_sk(sk)->gro_enabled) {
+               pp = call_gro_receive(udp_gro_receive_segment, head, skb);
+               rcu_read_unlock();
+               return pp;
+       }
+
        if (NAPI_GRO_CB(skb)->encap_mark ||
            (skb->ip_summed != CHECKSUM_PARTIAL &&
             NAPI_GRO_CB(skb)->csum_cnt == 0 &&
-            !NAPI_GRO_CB(skb)->csum_valid))
-               goto out;
+            !NAPI_GRO_CB(skb)->csum_valid) ||
+           !udp_sk(sk)->gro_receive)
+               goto out_unlock;
 
        /* mark that this skb passed once through the tunnel gro layer */
        NAPI_GRO_CB(skb)->encap_mark = 1;
 
-       rcu_read_lock();
-       sk = (*lookup)(skb, uh->source, uh->dest);
-
-       if (sk && udp_sk(sk)->gro_receive)
-               goto unflush;
-       goto out_unlock;
-
-unflush:
        flush = 0;
 
        list_for_each_entry(p, head, list) {
@@ -394,7 +446,6 @@ unflush:
 
 out_unlock:
        rcu_read_unlock();
-out:
        skb_gro_flush_final(skb, pp, flush);
        return pp;
 }
@@ -427,6 +478,19 @@ flush:
        return NULL;
 }
 
+static int udp_gro_complete_segment(struct sk_buff *skb)
+{
+       struct udphdr *uh = udp_hdr(skb);
+
+       skb->csum_start = (unsigned char *)uh - skb->head;
+       skb->csum_offset = offsetof(struct udphdr, check);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+
+       skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+       skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_L4;
+       return 0;
+}
+
 int udp_gro_complete(struct sk_buff *skb, int nhoff,
                     udp_lookup_t lookup)
 {
@@ -437,16 +501,21 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff,
 
        uh->len = newlen;
 
-       /* Set encapsulation before calling into inner gro_complete() functions
-        * to make them set up the inner offsets.
-        */
-       skb->encapsulation = 1;
-
        rcu_read_lock();
        sk = (*lookup)(skb, uh->source, uh->dest);
-       if (sk && udp_sk(sk)->gro_complete)
+       if (sk && udp_sk(sk)->gro_enabled) {
+               err = udp_gro_complete_segment(skb);
+       } else if (sk && udp_sk(sk)->gro_complete) {
+               skb_shinfo(skb)->gso_type = uh->check ? SKB_GSO_UDP_TUNNEL_CSUM
+                                       : SKB_GSO_UDP_TUNNEL;
+
+               /* Set encapsulation before calling into inner gro_complete()
+                * functions to make them set up the inner offsets.
+                */
+               skb->encapsulation = 1;
                err = udp_sk(sk)->gro_complete(sk, skb,
                                nhoff + sizeof(struct udphdr));
+       }
        rcu_read_unlock();
 
        if (skb->remcsum_offload)
@@ -461,13 +530,9 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
        const struct iphdr *iph = ip_hdr(skb);
        struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
 
-       if (uh->check) {
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
+       if (uh->check)
                uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr,
                                          iph->daddr, 0);
-       } else {
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
-       }
 
        return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
 }
index 6539ff1..be8b5b2 100644 (file)
@@ -20,6 +20,23 @@ int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
        if (err < 0)
                goto error;
 
+       if (cfg->bind_ifindex) {
+               struct net_device *dev;
+
+               dev = dev_get_by_index(net, cfg->bind_ifindex);
+               if (!dev) {
+                       err = -ENODEV;
+                       goto error;
+               }
+
+               err = kernel_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                                       dev->name, strlen(dev->name) + 1);
+               dev_put(dev);
+
+               if (err < 0)
+                       goto error;
+       }
+
        udp_addr.sin_family = AF_INET;
        udp_addr.sin_addr = cfg->local_ip;
        udp_addr.sin_port = cfg->local_udp_port;
@@ -68,6 +85,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 
        udp_sk(sk)->encap_type = cfg->encap_type;
        udp_sk(sk)->encap_rcv = cfg->encap_rcv;
+       udp_sk(sk)->encap_err_lookup = cfg->encap_err_lookup;
        udp_sk(sk)->encap_destroy = cfg->encap_destroy;
        udp_sk(sk)->gro_receive = cfg->gro_receive;
        udp_sk(sk)->gro_complete = cfg->gro_complete;
index 8545457..39c7f17 100644 (file)
@@ -25,9 +25,9 @@ static int udplite_rcv(struct sk_buff *skb)
        return __udp4_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
 }
 
-static void udplite_err(struct sk_buff *skb, u32 info)
+static int udplite_err(struct sk_buff *skb, u32 info)
 {
-       __udp4_lib_err(skb, info, &udplite_table);
+       return __udp4_lib_err(skb, info, &udplite_table);
 }
 
 static const struct net_protocol udplite_protocol = {
index 8dd0e6a..35c5486 100644 (file)
@@ -106,13 +106,15 @@ static int xfrm4_esp_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm4_esp_err(struct sk_buff *skb, u32 info)
+static int xfrm4_esp_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm4_protocol *handler;
 
        for_each_protocol_rcu(esp4_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static int xfrm4_ah_rcv(struct sk_buff *skb)
@@ -132,13 +134,15 @@ static int xfrm4_ah_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm4_ah_err(struct sk_buff *skb, u32 info)
+static int xfrm4_ah_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm4_protocol *handler;
 
        for_each_protocol_rcu(ah4_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static int xfrm4_ipcomp_rcv(struct sk_buff *skb)
@@ -158,13 +162,15 @@ static int xfrm4_ipcomp_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm4_ipcomp_err(struct sk_buff *skb, u32 info)
+static int xfrm4_ipcomp_err(struct sk_buff *skb, u32 info)
 {
        struct xfrm4_protocol *handler;
 
        for_each_protocol_rcu(ipcomp4_handlers, handler)
                if (!handler->err_handler(skb, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static const struct net_protocol esp4_protocol = {
index 045597b..521e471 100644 (file)
@@ -2820,7 +2820,7 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
                        dev = __dev_get_by_name(net, p.name);
                        if (!dev)
                                goto err_exit;
-                       err = dev_open(dev);
+                       err = dev_open(dev, NULL);
                }
        }
 #endif
index 9499905..cca3b36 100644 (file)
@@ -433,7 +433,6 @@ static bool ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *ad
 bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
                         const struct in6_addr *addr)
 {
-       unsigned int hash = inet6_acaddr_hash(net, addr);
        struct net_device *nh_dev;
        struct ifacaddr6 *aca;
        bool found = false;
@@ -441,7 +440,9 @@ bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
        rcu_read_lock();
        if (dev)
                found = ipv6_chk_acast_dev(dev, addr);
-       else
+       else {
+               unsigned int hash = inet6_acaddr_hash(net, addr);
+
                hlist_for_each_entry_rcu(aca, &inet6_acaddr_lst[hash],
                                         aca_addr_lst) {
                        nh_dev = fib6_info_nh_dev(aca->aca_rt);
@@ -452,6 +453,7 @@ bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
                                break;
                        }
                }
+       }
        rcu_read_unlock();
        return found;
 }
index 1ede7a1..bde08aa 100644 (file)
@@ -772,6 +772,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
                case IPV6_2292PKTINFO:
                    {
                        struct net_device *dev = NULL;
+                       int src_idx;
 
                        if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
                                err = -EINVAL;
@@ -779,12 +780,15 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
                        }
 
                        src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+                       src_idx = src_info->ipi6_ifindex;
 
-                       if (src_info->ipi6_ifindex) {
+                       if (src_idx) {
                                if (fl6->flowi6_oif &&
-                                   src_info->ipi6_ifindex != fl6->flowi6_oif)
+                                   src_idx != fl6->flowi6_oif &&
+                                   (sk->sk_bound_dev_if != fl6->flowi6_oif ||
+                                    !sk_dev_equal_l3scope(sk, src_idx)))
                                        return -EINVAL;
-                               fl6->flowi6_oif = src_info->ipi6_ifindex;
+                               fl6->flowi6_oif = src_idx;
                        }
 
                        addr_type = __ipv6_addr_type(&src_info->ipi6_addr);
index 6de3c04..bd675c6 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/skbuff.h>
 #include <linux/ip.h>
 #include <linux/udp.h>
+#include <linux/icmpv6.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <net/fou.h>
@@ -69,14 +70,87 @@ static int gue6_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
        return 0;
 }
 
+static int gue6_err_proto_handler(int proto, struct sk_buff *skb,
+                                 struct inet6_skb_parm *opt,
+                                 u8 type, u8 code, int offset, u32 info)
+{
+       const struct inet6_protocol *ipprot;
+
+       ipprot = rcu_dereference(inet6_protos[proto]);
+       if (ipprot && ipprot->err_handler) {
+               if (!ipprot->err_handler(skb, opt, type, code, offset, info))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+static int gue6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                   u8 type, u8 code, int offset, __be32 info)
+{
+       int transport_offset = skb_transport_offset(skb);
+       struct guehdr *guehdr;
+       size_t optlen;
+       int ret;
+
+       if (skb->len < sizeof(struct udphdr) + sizeof(struct guehdr))
+               return -EINVAL;
+
+       guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+       switch (guehdr->version) {
+       case 0: /* Full GUE header present */
+               break;
+       case 1: {
+               /* Direct encasulation of IPv4 or IPv6 */
+               skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
+
+               switch (((struct iphdr *)guehdr)->version) {
+               case 4:
+                       ret = gue6_err_proto_handler(IPPROTO_IPIP, skb, opt,
+                                                    type, code, offset, info);
+                       goto out;
+               case 6:
+                       ret = gue6_err_proto_handler(IPPROTO_IPV6, skb, opt,
+                                                    type, code, offset, info);
+                       goto out;
+               default:
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
+       }
+       default: /* Undefined version */
+               return -EOPNOTSUPP;
+       }
+
+       if (guehdr->control)
+               return -ENOENT;
+
+       optlen = guehdr->hlen << 2;
+
+       if (validate_gue_flags(guehdr, optlen))
+               return -EINVAL;
+
+       skb_set_transport_header(skb, -(int)sizeof(struct icmp6hdr));
+       ret = gue6_err_proto_handler(guehdr->proto_ctype, skb,
+                                    opt, type, code, offset, info);
+
+out:
+       skb_set_transport_header(skb, transport_offset);
+       return ret;
+}
+
+
 static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
        .encap_hlen = fou_encap_hlen,
        .build_header = fou6_build_header,
+       .err_handler = gue6_err,
 };
 
 static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
        .encap_hlen = gue_encap_hlen,
        .build_header = gue6_build_header,
+       .err_handler = gue6_err,
 };
 
 static int ip6_tnl_encap_add_fou_ops(void)
index c9c53ad..5d7aa2c 100644 (file)
@@ -84,7 +84,7 @@ static inline struct sock *icmpv6_sk(struct net *net)
        return net->ipv6.icmp_sk[smp_processor_id()];
 }
 
-static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                       u8 type, u8 code, int offset, __be32 info)
 {
        /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
@@ -100,6 +100,8 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        if (!(type & ICMPV6_INFOMSG_MASK))
                if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
                        ping_err(skb, offset, ntohl(info));
+
+       return 0;
 }
 
 static int icmpv6_rcv(struct sk_buff *skb);
index 3d7c746..5eeeba7 100644 (file)
@@ -99,6 +99,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                const int dif, const int sdif, bool exact_dif)
 {
        int score = -1;
+       bool dev_match;
 
        if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum &&
            sk->sk_family == PF_INET6) {
@@ -109,15 +110,12 @@ static inline int compute_score(struct sock *sk, struct net *net,
                                return -1;
                        score++;
                }
-               if (sk->sk_bound_dev_if || exact_dif) {
-                       bool dev_match = (sk->sk_bound_dev_if == dif ||
-                                         sk->sk_bound_dev_if == sdif);
+               dev_match = inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+                                                dif, sdif);
+               if (!dev_match)
+                       return -1;
+               score++;
 
-                       if (!dev_match)
-                               return -1;
-                       if (sk->sk_bound_dev_if)
-                               score++;
-               }
                if (sk->sk_incoming_cpu == raw_smp_processor_id())
                        score++;
        }
index 515adbd..81b69bc 100644 (file)
@@ -423,7 +423,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
 }
 
 
-static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                       u8 type, u8 code, int offset, __be32 info)
 {
        struct net *net = dev_net(skb->dev);
@@ -433,13 +433,13 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IPV6),
                             offset) < 0)
-               return;
+               return -EINVAL;
 
        ipv6h = (const struct ipv6hdr *)skb->data;
        t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
                                 tpi.key, tpi.proto);
        if (!t)
-               return;
+               return -ENOENT;
 
        switch (type) {
                struct ipv6_tlv_tnl_enc_lim *tel;
@@ -449,14 +449,14 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                                    t->parms.name);
                if (code != ICMPV6_PORT_UNREACH)
                        break;
-               return;
+               return 0;
        case ICMPV6_TIME_EXCEED:
                if (code == ICMPV6_EXC_HOPLIMIT) {
                        net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
                                            t->parms.name);
                        break;
                }
-               return;
+               return 0;
        case ICMPV6_PARAMPROB:
                teli = 0;
                if (code == ICMPV6_HDR_FIELD)
@@ -472,14 +472,14 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
                                            t->parms.name);
                }
-               return;
+               return 0;
        case ICMPV6_PKT_TOOBIG:
                ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
-               return;
+               return 0;
        case NDISC_REDIRECT:
                ip6_redirect(skb, net, skb->dev->ifindex, 0,
                             sock_net_uid(net, NULL));
-               return;
+               return 0;
        }
 
        if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
@@ -487,6 +487,8 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        else
                t->err_count = 1;
        t->err_time = jiffies;
+
+       return 0;
 }
 
 static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
index c1d8583..c7ed2b6 100644 (file)
@@ -319,28 +319,26 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
 /*
  *     Deliver the packet to the host
  */
-
-
-static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
+                             bool have_final)
 {
        const struct inet6_protocol *ipprot;
        struct inet6_dev *idev;
        unsigned int nhoff;
-       int nexthdr;
        bool raw;
-       bool have_final = false;
 
        /*
         *      Parse extension headers
         */
 
-       rcu_read_lock();
 resubmit:
        idev = ip6_dst_idev(skb_dst(skb));
-       if (!pskb_pull(skb, skb_transport_offset(skb)))
-               goto discard;
        nhoff = IP6CB(skb)->nhoff;
-       nexthdr = skb_network_header(skb)[nhoff];
+       if (!have_final) {
+               if (!pskb_pull(skb, skb_transport_offset(skb)))
+                       goto discard;
+               nexthdr = skb_network_header(skb)[nhoff];
+       }
 
 resubmit_final:
        raw = raw6_local_deliver(skb, nexthdr);
@@ -359,6 +357,8 @@ resubmit_final:
                        }
                } else if (ipprot->flags & INET6_PROTO_FINAL) {
                        const struct ipv6hdr *hdr;
+                       int sdif = inet6_sdif(skb);
+                       struct net_device *dev;
 
                        /* Only do this once for first final protocol */
                        have_final = true;
@@ -371,9 +371,19 @@ resubmit_final:
                        skb_postpull_rcsum(skb, skb_network_header(skb),
                                           skb_network_header_len(skb));
                        hdr = ipv6_hdr(skb);
+
+                       /* skb->dev passed may be master dev for vrfs. */
+                       if (sdif) {
+                               dev = dev_get_by_index_rcu(net, sdif);
+                               if (!dev)
+                                       goto discard;
+                       } else {
+                               dev = skb->dev;
+                       }
+
                        if (ipv6_addr_is_multicast(&hdr->daddr) &&
-                           !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
-                           &hdr->saddr) &&
+                           !ipv6_chk_mcast_addr(dev, &hdr->daddr,
+                                                &hdr->saddr) &&
                            !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb)))
                                goto discard;
                }
@@ -411,13 +421,19 @@ resubmit_final:
                        consume_skb(skb);
                }
        }
-       rcu_read_unlock();
-       return 0;
+       return;
 
 discard:
        __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
-       rcu_read_unlock();
        kfree_skb(skb);
+}
+
+static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       rcu_read_lock();
+       ip6_protocol_deliver_rcu(net, skb, 0, false);
+       rcu_read_unlock();
+
        return 0;
 }
 
@@ -432,15 +448,32 @@ EXPORT_SYMBOL_GPL(ip6_input);
 
 int ip6_mc_input(struct sk_buff *skb)
 {
+       int sdif = inet6_sdif(skb);
        const struct ipv6hdr *hdr;
+       struct net_device *dev;
        bool deliver;
 
        __IP6_UPD_PO_STATS(dev_net(skb_dst(skb)->dev),
                         __in6_dev_get_safely(skb->dev), IPSTATS_MIB_INMCAST,
                         skb->len);
 
+       /* skb->dev passed may be master dev for vrfs. */
+       if (sdif) {
+               rcu_read_lock();
+               dev = dev_get_by_index_rcu(dev_net(skb->dev), sdif);
+               if (!dev) {
+                       rcu_read_unlock();
+                       kfree_skb(skb);
+                       return -ENODEV;
+               }
+       } else {
+               dev = skb->dev;
+       }
+
        hdr = ipv6_hdr(skb);
-       deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
+       deliver = ipv6_chk_mcast_addr(dev, &hdr->daddr, NULL);
+       if (sdif)
+               rcu_read_unlock();
 
 #ifdef CONFIG_IPV6_MROUTE
        /*
index c7e495f..70f525c 100644 (file)
@@ -229,14 +229,21 @@ static struct sk_buff *ipv6_gro_receive(struct list_head *head,
                 * XXX skbs on the gro_list have all been parsed and pulled
                 * already so we don't need to compare nlen
                 * (nlen != (sizeof(*iph2) + ipv6_exthdrs_len(iph2, &ops)))
-                * memcmp() alone below is suffcient, right?
+                * memcmp() alone below is sufficient, right?
                 */
                 if ((first_word & htonl(0xF00FFFFF)) ||
-                   memcmp(&iph->nexthdr, &iph2->nexthdr,
-                          nlen - offsetof(struct ipv6hdr, nexthdr))) {
+                   !ipv6_addr_equal(&iph->saddr, &iph2->saddr) ||
+                   !ipv6_addr_equal(&iph->daddr, &iph2->daddr) ||
+                   *(u16 *)&iph->nexthdr != *(u16 *)&iph2->nexthdr) {
+not_same_flow:
                        NAPI_GRO_CB(p)->same_flow = 0;
                        continue;
                }
+               if (unlikely(nlen > sizeof(struct ipv6hdr))) {
+                       if (memcmp(iph + 1, iph2 + 1,
+                                  nlen - sizeof(struct ipv6hdr)))
+                               goto not_same_flow;
+               }
                /* flush if Traffic Class fields are different */
                NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
                NAPI_GRO_CB(p)->flush |= flush;
index fcd3c66..9d55ee3 100644 (file)
@@ -378,6 +378,13 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk,
        __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
        __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       if (skb->offload_l3_fwd_mark) {
+               consume_skb(skb);
+               return 0;
+       }
+#endif
+
        return dst_output(net, sk, skb);
 }
 
@@ -1245,6 +1252,7 @@ static int __ip6_append_data(struct sock *sk,
 {
        struct sk_buff *skb, *skb_prev = NULL;
        unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu;
+       struct ubuf_info *uarg = NULL;
        int exthdrlen = 0;
        int dst_exthdrlen = 0;
        int hh_len;
@@ -1257,7 +1265,7 @@ static int __ip6_append_data(struct sock *sk,
        int csummode = CHECKSUM_NONE;
        unsigned int maxnonfragsize, headersize;
        unsigned int wmem_alloc_delta = 0;
-       bool paged;
+       bool paged, extra_uref;
 
        skb = skb_peek_tail(queue);
        if (!skb) {
@@ -1322,6 +1330,20 @@ emsgsize:
            rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
                csummode = CHECKSUM_PARTIAL;
 
+       if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
+               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               if (!uarg)
+                       return -ENOBUFS;
+               extra_uref = true;
+               if (rt->dst.dev->features & NETIF_F_SG &&
+                   csummode == CHECKSUM_PARTIAL) {
+                       paged = true;
+               } else {
+                       uarg->zerocopy = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+               }
+       }
+
        /*
         * Let's try using as much space as possible.
         * Use MTU if total length of the message fits into the MTU.
@@ -1440,12 +1462,6 @@ alloc_new_skb:
                        skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
                                    dst_exthdrlen);
 
-                       /* Only the initial fragment is time stamped */
-                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
-                       cork->tx_flags = 0;
-                       skb_shinfo(skb)->tskey = tskey;
-                       tskey = 0;
-
                        /*
                         *      Find where to start putting bytes
                         */
@@ -1477,6 +1493,13 @@ alloc_new_skb:
                        exthdrlen = 0;
                        dst_exthdrlen = 0;
 
+                       /* Only the initial fragment is time stamped */
+                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
+                       cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+
                        if ((flags & MSG_CONFIRM) && !skb_prev)
                                skb_set_dst_pending_confirm(skb, 1);
 
@@ -1506,7 +1529,7 @@ alloc_new_skb:
                                err = -EFAULT;
                                goto error;
                        }
-               } else {
+               } else if (!uarg || !uarg->zerocopy) {
                        int i = skb_shinfo(skb)->nr_frags;
 
                        err = -ENOMEM;
@@ -1536,6 +1559,10 @@ alloc_new_skb:
                        skb->data_len += copy;
                        skb->truesize += copy;
                        wmem_alloc_delta += copy;
+               } else {
+                       err = skb_zerocopy_iter_dgram(skb, from, copy);
+                       if (err < 0)
+                               goto error;
                }
                offset += copy;
                length -= copy;
@@ -1548,6 +1575,8 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
+       if (uarg)
+               sock_zerocopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index b283f29..3965d53 100644 (file)
@@ -31,6 +31,22 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
                if (err < 0)
                        goto error;
        }
+       if (cfg->bind_ifindex) {
+               struct net_device *dev;
+
+               dev = dev_get_by_index(net, cfg->bind_ifindex);
+               if (!dev) {
+                       err = -ENODEV;
+                       goto error;
+               }
+
+               err = kernel_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+                                       dev->name, strlen(dev->name) + 1);
+               dev_put(dev);
+
+               if (err < 0)
+                       goto error;
+       }
 
        udp6_addr.sin6_family = AF_INET6;
        memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
index e2ea691..8c63494 100644 (file)
@@ -655,7 +655,7 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
                return NULL;
        }
 
-       if (dev_open(dev))
+       if (dev_open(dev, NULL))
                goto failure;
 
        dev_hold(dev);
index 381ce38..973e215 100644 (file)
@@ -486,7 +486,7 @@ sticky_done:
                                retv = -EFAULT;
                                break;
                }
-               if (sk->sk_bound_dev_if && pkt.ipi6_ifindex != sk->sk_bound_dev_if)
+               if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
                        goto e_inval;
 
                np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
index 5e0efd3..aed7eb5 100644 (file)
@@ -86,9 +86,8 @@ struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
                            !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
                                continue;
 
-                       if (sk->sk_bound_dev_if &&
-                           sk->sk_bound_dev_if != dif &&
-                           sk->sk_bound_dev_if != sdif)
+                       if (!raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+                                                dif, sdif))
                                continue;
 
                        if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
index 059f053..194bc16 100644 (file)
@@ -2977,7 +2977,8 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
        if (!rt)
                goto out;
 
-       rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len);
+       rt->fib6_metrics = ip_fib_metrics_init(net, cfg->fc_mx, cfg->fc_mx_len,
+                                              extack);
        if (IS_ERR(rt->fib6_metrics)) {
                err = PTR_ERR(rt->fib6_metrics);
                /* Do not leave garbage there. */
@@ -3710,7 +3711,7 @@ struct fib6_info *addrconf_f6i_alloc(struct net *net,
        if (!f6i)
                return ERR_PTR(-ENOMEM);
 
-       f6i->fib6_metrics = ip_fib_metrics_init(net, NULL, 0);
+       f6i->fib6_metrics = ip_fib_metrics_init(net, NULL, 0, NULL);
        f6i->dst_nocount = true;
        f6i->dst_host = true;
        f6i->fib6_protocol = RTPROT_KERNEL;
index 03e6b7a..a3f5591 100644 (file)
@@ -349,7 +349,7 @@ static void tcp_v6_mtu_reduced(struct sock *sk)
        }
 }
 
-static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                u8 type, u8 code, int offset, __be32 info)
 {
        const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;
@@ -371,17 +371,19 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        if (!sk) {
                __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
                                  ICMP6_MIB_INERRORS);
-               return;
+               return -ENOENT;
        }
 
        if (sk->sk_state == TCP_TIME_WAIT) {
                inet_twsk_put(inet_twsk(sk));
-               return;
+               return 0;
        }
        seq = ntohl(th->seq);
        fatal = icmpv6_err_convert(type, code, &err);
-       if (sk->sk_state == TCP_NEW_SYN_RECV)
-               return tcp_req_err(sk, seq, fatal);
+       if (sk->sk_state == TCP_NEW_SYN_RECV) {
+               tcp_req_err(sk, seq, fatal);
+               return 0;
+       }
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG)
@@ -467,6 +469,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 out:
        bh_unlock_sock(sk);
        sock_put(sk);
+       return 0;
 }
 
 
index dae25ca..1991ded 100644 (file)
@@ -134,24 +134,28 @@ drop:
        return 0;
 }
 
-static void tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        u8 type, u8 code, int offset, __be32 info)
 {
        struct xfrm6_tunnel *handler;
 
        for_each_tunnel_rcu(tunnel6_handlers, handler)
                if (!handler->err_handler(skb, opt, type, code, offset, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
-static void tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                         u8 type, u8 code, int offset, __be32 info)
 {
        struct xfrm6_tunnel *handler;
 
        for_each_tunnel_rcu(tunnel46_handlers, handler)
                if (!handler->err_handler(skb, opt, type, code, offset, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static const struct inet6_protocol tunnel6_protocol = {
index d2d97d0..09cba4c 100644 (file)
@@ -45,6 +45,7 @@
 #include <net/raw.h>
 #include <net/tcp_states.h>
 #include <net/ip6_checksum.h>
+#include <net/ip6_tunnel.h>
 #include <net/xfrm.h>
 #include <net/inet_hashtables.h>
 #include <net/inet6_hashtables.h>
@@ -117,6 +118,7 @@ static int compute_score(struct sock *sk, struct net *net,
 {
        int score;
        struct inet_sock *inet;
+       bool dev_match;
 
        if (!net_eq(sock_net(sk), net) ||
            udp_sk(sk)->udp_port_hash != hnum ||
@@ -144,15 +146,10 @@ static int compute_score(struct sock *sk, struct net *net,
                score++;
        }
 
-       if (sk->sk_bound_dev_if || exact_dif) {
-               bool dev_match = (sk->sk_bound_dev_if == dif ||
-                                 sk->sk_bound_dev_if == sdif);
-
-               if (!dev_match)
-                       return -1;
-               if (sk->sk_bound_dev_if)
-                       score++;
-       }
+       dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif);
+       if (!dev_match)
+               return -1;
+       score++;
 
        if (sk->sk_incoming_cpu == raw_smp_processor_id())
                score++;
@@ -329,6 +326,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        int err;
        int is_udplite = IS_UDPLITE(sk);
        bool checksum_valid = false;
+       struct udp_mib *mib;
        int is_udp4;
 
        if (flags & MSG_ERRQUEUE)
@@ -352,6 +350,7 @@ try_again:
                msg->msg_flags |= MSG_TRUNC;
 
        is_udp4 = (skb->protocol == htons(ETH_P_IP));
+       mib = __UDPX_MIB(sk, is_udp4);
 
        /*
         * If checksum is needed at all, try to do it while copying the
@@ -380,24 +379,13 @@ try_again:
        if (unlikely(err)) {
                if (!peeked) {
                        atomic_inc(&sk->sk_drops);
-                       if (is_udp4)
-                               UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS,
-                                             is_udplite);
-                       else
-                               UDP6_INC_STATS(sock_net(sk), UDP_MIB_INERRORS,
-                                              is_udplite);
+                       SNMP_INC_STATS(mib, UDP_MIB_INERRORS);
                }
                kfree_skb(skb);
                return err;
        }
-       if (!peeked) {
-               if (is_udp4)
-                       UDP_INC_STATS(sock_net(sk), UDP_MIB_INDATAGRAMS,
-                                     is_udplite);
-               else
-                       UDP6_INC_STATS(sock_net(sk), UDP_MIB_INDATAGRAMS,
-                                      is_udplite);
-       }
+       if (!peeked)
+               SNMP_INC_STATS(mib, UDP_MIB_INDATAGRAMS);
 
        sock_recv_ts_and_drops(msg, sk, skb);
 
@@ -421,6 +409,9 @@ try_again:
                *addr_len = sizeof(*sin6);
        }
 
+       if (udp_sk(sk)->gro_enabled)
+               udp_cmsg_recv(msg, sk, skb);
+
        if (np->rxopt.all)
                ip6_datagram_recv_common_ctl(sk, msg, skb);
 
@@ -443,17 +434,8 @@ try_again:
 csum_copy_err:
        if (!__sk_queue_drop_skb(sk, &udp_sk(sk)->reader_queue, skb, flags,
                                 udp_skb_destructor)) {
-               if (is_udp4) {
-                       UDP_INC_STATS(sock_net(sk),
-                                     UDP_MIB_CSUMERRORS, is_udplite);
-                       UDP_INC_STATS(sock_net(sk),
-                                     UDP_MIB_INERRORS, is_udplite);
-               } else {
-                       UDP6_INC_STATS(sock_net(sk),
-                                      UDP_MIB_CSUMERRORS, is_udplite);
-                       UDP6_INC_STATS(sock_net(sk),
-                                      UDP_MIB_INERRORS, is_udplite);
-               }
+               SNMP_INC_STATS(mib, UDP_MIB_CSUMERRORS);
+               SNMP_INC_STATS(mib, UDP_MIB_INERRORS);
        }
        kfree_skb(skb);
 
@@ -463,15 +445,106 @@ csum_copy_err:
        goto try_again;
 }
 
-void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-                   u8 type, u8 code, int offset, __be32 info,
-                   struct udp_table *udptable)
+DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
+void udpv6_encap_enable(void)
+{
+       static_branch_inc(&udpv6_encap_needed_key);
+}
+EXPORT_SYMBOL(udpv6_encap_enable);
+
+/* Handler for tunnels with arbitrary destination ports: no socket lookup, go
+ * through error handlers in encapsulations looking for a match.
+ */
+static int __udp6_lib_err_encap_no_sk(struct sk_buff *skb,
+                                     struct inet6_skb_parm *opt,
+                                     u8 type, u8 code, int offset, u32 info)
+{
+       int i;
+
+       for (i = 0; i < MAX_IPTUN_ENCAP_OPS; i++) {
+               int (*handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                              u8 type, u8 code, int offset, u32 info);
+
+               if (!ip6tun_encaps[i])
+                       continue;
+               handler = rcu_dereference(ip6tun_encaps[i]->err_handler);
+               if (handler && !handler(skb, opt, type, code, offset, info))
+                       return 0;
+       }
+
+       return -ENOENT;
+}
+
+/* Try to match ICMP errors to UDP tunnels by looking up a socket without
+ * reversing source and destination port: this will match tunnels that force the
+ * same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that
+ * lwtunnels might actually break this assumption by being configured with
+ * different destination ports on endpoints, in this case we won't be able to
+ * trace ICMP messages back to them.
+ *
+ * If this doesn't match any socket, probe tunnels with arbitrary destination
+ * ports (e.g. FoU, GUE): there, the receiving socket is useless, as the port
+ * we've sent packets to won't necessarily match the local destination port.
+ *
+ * Then ask the tunnel implementation to match the error against a valid
+ * association.
+ *
+ * Return an error if we can't find a match, the socket if we need further
+ * processing, zero otherwise.
+ */
+static struct sock *__udp6_lib_err_encap(struct net *net,
+                                        const struct ipv6hdr *hdr, int offset,
+                                        struct udphdr *uh,
+                                        struct udp_table *udptable,
+                                        struct sk_buff *skb,
+                                        struct inet6_skb_parm *opt,
+                                        u8 type, u8 code, __be32 info)
+{
+       int network_offset, transport_offset;
+       struct sock *sk;
+
+       network_offset = skb_network_offset(skb);
+       transport_offset = skb_transport_offset(skb);
+
+       /* Network header needs to point to the outer IPv6 header inside ICMP */
+       skb_reset_network_header(skb);
+
+       /* Transport header needs to point to the UDP header */
+       skb_set_transport_header(skb, offset);
+
+       sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source,
+                              &hdr->saddr, uh->dest,
+                              inet6_iif(skb), 0, udptable, skb);
+       if (sk) {
+               int (*lookup)(struct sock *sk, struct sk_buff *skb);
+               struct udp_sock *up = udp_sk(sk);
+
+               lookup = READ_ONCE(up->encap_err_lookup);
+               if (!lookup || lookup(sk, skb))
+                       sk = NULL;
+       }
+
+       if (!sk) {
+               sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code,
+                                                       offset, info));
+       }
+
+       skb_set_transport_header(skb, transport_offset);
+       skb_set_network_header(skb, network_offset);
+
+       return sk;
+}
+
+int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+                  u8 type, u8 code, int offset, __be32 info,
+                  struct udp_table *udptable)
 {
        struct ipv6_pinfo *np;
        const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;
        const struct in6_addr *saddr = &hdr->saddr;
        const struct in6_addr *daddr = &hdr->daddr;
        struct udphdr *uh = (struct udphdr *)(skb->data+offset);
+       bool tunnel = false;
        struct sock *sk;
        int harderr;
        int err;
@@ -480,9 +553,23 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
                               inet6_iif(skb), inet6_sdif(skb), udptable, skb);
        if (!sk) {
-               __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
-                                 ICMP6_MIB_INERRORS);
-               return;
+               /* No socket for error: try tunnels before discarding */
+               sk = ERR_PTR(-ENOENT);
+               if (static_branch_unlikely(&udpv6_encap_needed_key)) {
+                       sk = __udp6_lib_err_encap(net, hdr, offset, uh,
+                                                 udptable, skb,
+                                                 opt, type, code, info);
+                       if (!sk)
+                               return 0;
+               }
+
+               if (IS_ERR(sk)) {
+                       __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
+                                         ICMP6_MIB_INERRORS);
+                       return PTR_ERR(sk);
+               }
+
+               tunnel = true;
        }
 
        harderr = icmpv6_err_convert(type, code, &err);
@@ -496,10 +583,19 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        harderr = 1;
        }
        if (type == NDISC_REDIRECT) {
-               ip6_sk_redirect(skb, sk);
+               if (tunnel) {
+                       ip6_redirect(skb, sock_net(sk), inet6_iif(skb),
+                                    sk->sk_mark, sk->sk_uid);
+               } else {
+                       ip6_sk_redirect(skb, sk);
+               }
                goto out;
        }
 
+       /* Tunnels don't have an application socket: don't pass errors back */
+       if (tunnel)
+               goto out;
+
        if (!np->recverr) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
@@ -510,7 +606,7 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        sk->sk_err = err;
        sk->sk_error_report(sk);
 out:
-       return;
+       return 0;
 }
 
 static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
@@ -541,21 +637,14 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
-static __inline__ void udpv6_err(struct sk_buff *skb,
-                                struct inet6_skb_parm *opt, u8 type,
-                                u8 code, int offset, __be32 info)
+static __inline__ int udpv6_err(struct sk_buff *skb,
+                               struct inet6_skb_parm *opt, u8 type,
+                               u8 code, int offset, __be32 info)
 {
-       __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table);
+       return __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table);
 }
 
-DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key);
-void udpv6_encap_enable(void)
-{
-       static_branch_enable(&udpv6_encap_needed_key);
-}
-EXPORT_SYMBOL(udpv6_encap_enable);
-
-static int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
 {
        struct udp_sock *up = udp_sk(sk);
        int is_udplite = IS_UDPLITE(sk);
@@ -638,10 +727,32 @@ drop:
        return -1;
 }
 
+static int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       struct sk_buff *next, *segs;
+       int ret;
+
+       if (likely(!udp_unexpected_gso(sk, skb)))
+               return udpv6_queue_rcv_one_skb(sk, skb);
+
+       __skb_push(skb, -skb_mac_offset(skb));
+       segs = udp_rcv_segment(sk, skb, false);
+       for (skb = segs; skb; skb = next) {
+               next = skb->next;
+               __skb_pull(skb, skb_transport_offset(skb));
+
+               ret = udpv6_queue_rcv_one_skb(sk, skb);
+               if (ret > 0)
+                       ip6_protocol_deliver_rcu(dev_net(skb->dev), skb, ret,
+                                                true);
+       }
+       return 0;
+}
+
 static bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk,
                                   __be16 loc_port, const struct in6_addr *loc_addr,
                                   __be16 rmt_port, const struct in6_addr *rmt_addr,
-                                  int dif, unsigned short hnum)
+                                  int dif, int sdif, unsigned short hnum)
 {
        struct inet_sock *inet = inet_sk(sk);
 
@@ -653,7 +764,7 @@ static bool __udp_v6_is_mcast_sock(struct net *net, struct sock *sk,
            (inet->inet_dport && inet->inet_dport != rmt_port) ||
            (!ipv6_addr_any(&sk->sk_v6_daddr) &&
                    !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) ||
-           (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) ||
+           !udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif) ||
            (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) &&
                    !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr)))
                return false;
@@ -687,6 +798,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
        unsigned int offset = offsetof(typeof(*sk), sk_node);
        unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10);
        int dif = inet6_iif(skb);
+       int sdif = inet6_sdif(skb);
        struct hlist_node *node;
        struct sk_buff *nskb;
 
@@ -701,7 +813,8 @@ start_lookup:
 
        sk_for_each_entry_offset_rcu(sk, node, &hslot->head, offset) {
                if (!__udp_v6_is_mcast_sock(net, sk, uh->dest, daddr,
-                                           uh->source, saddr, dif, hnum))
+                                           uh->source, saddr, dif, sdif,
+                                           hnum))
                        continue;
                /* If zero checksum and no_check is not on for
                 * the socket then skip it.
@@ -1458,11 +1571,15 @@ void udpv6_destroy_sock(struct sock *sk)
        udp_v6_flush_pending_frames(sk);
        release_sock(sk);
 
-       if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) {
-               void (*encap_destroy)(struct sock *sk);
-               encap_destroy = READ_ONCE(up->encap_destroy);
-               if (encap_destroy)
-                       encap_destroy(sk);
+       if (static_branch_unlikely(&udpv6_encap_needed_key)) {
+               if (up->encap_type) {
+                       void (*encap_destroy)(struct sock *sk);
+                       encap_destroy = READ_ONCE(up->encap_destroy);
+                       if (encap_destroy)
+                               encap_destroy(sk);
+               }
+               if (up->encap_enabled)
+                       static_branch_dec(&udpv6_encap_needed_key);
        }
 
        inet6_destroy_sock(sk);
index 7903e21..5730e65 100644 (file)
@@ -9,8 +9,8 @@
 #include <net/transp_v6.h>
 
 int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int);
-void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int,
-                   __be32, struct udp_table *);
+int __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int,
+                  __be32, struct udp_table *);
 
 int udp_v6_get_port(struct sock *sk, unsigned short snum);
 
index 1b8e161..828b245 100644 (file)
@@ -147,13 +147,9 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
        const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
 
-       if (uh->check) {
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
+       if (uh->check)
                uh->check = ~udp_v6_check(skb->len - nhoff, &ipv6h->saddr,
                                          &ipv6h->daddr, 0);
-       } else {
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
-       }
 
        return udp_gro_complete(skb, nhoff, udp6_lib_lookup_skb);
 }
index 5000ad6..a125aeb 100644 (file)
@@ -20,11 +20,12 @@ static int udplitev6_rcv(struct sk_buff *skb)
        return __udp6_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
 }
 
-static void udplitev6_err(struct sk_buff *skb,
+static int udplitev6_err(struct sk_buff *skb,
                          struct inet6_skb_parm *opt,
                          u8 type, u8 code, int offset, __be32 info)
 {
-       __udp6_lib_err(skb, opt, type, code, offset, info, &udplite_table);
+       return __udp6_lib_err(skb, opt, type, code, offset, info,
+                             &udplite_table);
 }
 
 static const struct inet6_protocol udplitev6_protocol = {
index b2dc8ce..cc979b7 100644 (file)
@@ -80,14 +80,16 @@ static int xfrm6_esp_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                          u8 type, u8 code, int offset, __be32 info)
 {
        struct xfrm6_protocol *handler;
 
        for_each_protocol_rcu(esp6_handlers, handler)
                if (!handler->err_handler(skb, opt, type, code, offset, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static int xfrm6_ah_rcv(struct sk_buff *skb)
@@ -107,14 +109,16 @@ static int xfrm6_ah_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                         u8 type, u8 code, int offset, __be32 info)
 {
        struct xfrm6_protocol *handler;
 
        for_each_protocol_rcu(ah6_handlers, handler)
                if (!handler->err_handler(skb, opt, type, code, offset, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
@@ -134,14 +138,16 @@ static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
        return 0;
 }
 
-static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                             u8 type, u8 code, int offset, __be32 info)
 {
        struct xfrm6_protocol *handler;
 
        for_each_protocol_rcu(ipcomp6_handlers, handler)
                if (!handler->err_handler(skb, opt, type, code, offset, info))
-                       break;
+                       return 0;
+
+       return -ENOENT;
 }
 
 static const struct inet6_protocol esp6_protocol = {
index 0bed4cc..78ea5a7 100644 (file)
@@ -1873,30 +1873,26 @@ static void iucv_callback_txdone(struct iucv_path *path,
        struct sock *sk = path->private;
        struct sk_buff *this = NULL;
        struct sk_buff_head *list = &iucv_sk(sk)->send_skb_q;
-       struct sk_buff *list_skb = list->next;
+       struct sk_buff *list_skb;
        unsigned long flags;
 
        bh_lock_sock(sk);
-       if (!skb_queue_empty(list)) {
-               spin_lock_irqsave(&list->lock, flags);
 
-               while (list_skb != (struct sk_buff *)list) {
-                       if (msg->tag == IUCV_SKB_CB(list_skb)->tag) {
-                               this = list_skb;
-                               break;
-                       }
-                       list_skb = list_skb->next;
+       spin_lock_irqsave(&list->lock, flags);
+       skb_queue_walk(list, list_skb) {
+               if (msg->tag == IUCV_SKB_CB(list_skb)->tag) {
+                       this = list_skb;
+                       break;
                }
-               if (this)
-                       __skb_unlink(this, list);
-
-               spin_unlock_irqrestore(&list->lock, flags);
+       }
+       if (this)
+               __skb_unlink(this, list);
+       spin_unlock_irqrestore(&list->lock, flags);
 
-               if (this) {
-                       kfree_skb(this);
-                       /* wake up any process waiting for sending */
-                       iucv_sock_wake_msglim(sk);
-               }
+       if (this) {
+               kfree_skb(this);
+               /* wake up any process waiting for sending */
+               iucv_sock_wake_msglim(sk);
        }
 
        if (sk->sk_state == IUCV_CLOSING) {
@@ -2284,11 +2280,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
 
        list = &iucv->send_skb_q;
        spin_lock_irqsave(&list->lock, flags);
-       if (skb_queue_empty(list))
-               goto out_unlock;
-       list_skb = list->next;
-       nskb = list_skb->next;
-       while (list_skb != (struct sk_buff *)list) {
+       skb_queue_walk_safe(list, list_skb, nskb) {
                if (skb_shinfo(list_skb) == skb_shinfo(skb)) {
                        switch (n) {
                        case TX_NOTIFY_OK:
@@ -2321,10 +2313,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
                        }
                        break;
                }
-               list_skb = nskb;
-               nskb = nskb->next;
        }
-out_unlock:
        spin_unlock_irqrestore(&list->lock, flags);
 
        if (sk->sk_state == IUCV_CLOSING) {
index 8da86ce..309dee7 100644 (file)
@@ -47,6 +47,24 @@ int l3mdev_master_ifindex_rcu(const struct net_device *dev)
 EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
 
 /**
+ *     l3mdev_master_upper_ifindex_by_index - get index of upper l3 master
+ *                                            device
+ *     @net: network namespace for device index lookup
+ *     @ifindex: targeted interface
+ */
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
+{
+       struct net_device *dev;
+
+       dev = dev_get_by_index_rcu(net, ifindex);
+       while (dev && !netif_is_l3_master(dev))
+               dev = netdev_master_upper_dev_get(dev);
+
+       return dev ? dev->ifindex : 0;
+}
+EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu);
+
+/**
  *     l3mdev_fib_table - get FIB table id associated with an L3
  *                             master interface
  *     @dev: targeted interface
index 1dae77c..8750560 100644 (file)
@@ -73,10 +73,15 @@ enum {
 #define NCSI_OEM_MFR_BCM_ID             0x113d
 /* Broadcom specific OEM Command */
 #define NCSI_OEM_BCM_CMD_GMA            0x01   /* CMD ID for Get MAC */
+/* Mellanox specific OEM Command */
+#define NCSI_OEM_MLX_CMD_GMA            0x00   /* CMD ID for Get MAC */
+#define NCSI_OEM_MLX_CMD_GMA_PARAM      0x1b   /* Parameter for GMA  */
 /* OEM Command payload lengths*/
 #define NCSI_OEM_BCM_CMD_GMA_LEN        12
+#define NCSI_OEM_MLX_CMD_GMA_LEN        8
 /* Mac address offset in OEM response */
 #define BCM_MAC_ADDR_OFFSET             28
+#define MLX_MAC_ADDR_OFFSET             8
 
 
 struct ncsi_channel_version {
@@ -222,6 +227,10 @@ struct ncsi_package {
        unsigned int         channel_num; /* Number of channels     */
        struct list_head     channels;    /* List of chanels        */
        struct list_head     node;        /* Form list of packages  */
+
+       bool                 multi_channel; /* Enable multiple channels  */
+       u32                  channel_whitelist; /* Channels to configure */
+       struct ncsi_channel  *preferred_channel; /* Primary channel      */
 };
 
 struct ncsi_request {
@@ -287,16 +296,16 @@ struct ncsi_dev_priv {
 #define NCSI_DEV_PROBED                1            /* Finalized NCSI topology    */
 #define NCSI_DEV_HWA           2            /* Enabled HW arbitration     */
 #define NCSI_DEV_RESHUFFLE     4
+#define NCSI_DEV_RESET         8            /* Reset state of NC          */
        unsigned int        gma_flag;        /* OEM GMA flag               */
        spinlock_t          lock;            /* Protect the NCSI device    */
 #if IS_ENABLED(CONFIG_IPV6)
        unsigned int        inet6_addr_num;  /* Number of IPv6 addresses   */
 #endif
+       unsigned int        package_probe_id;/* Current ID during probe    */
        unsigned int        package_num;     /* Number of packages         */
        struct list_head    packages;        /* List of packages           */
        struct ncsi_channel *hot_channel;    /* Channel was ever active    */
-       struct ncsi_package *force_package;  /* Force a specific package   */
-       struct ncsi_channel *force_channel;  /* Force a specific channel   */
        struct ncsi_request requests[256];   /* Request table              */
        unsigned int        request_id;      /* Last used request ID       */
 #define NCSI_REQ_START_IDX     1
@@ -309,6 +318,9 @@ struct ncsi_dev_priv {
        struct list_head    node;            /* Form NCSI device list      */
 #define NCSI_MAX_VLAN_VIDS     15
        struct list_head    vlan_vids;       /* List of active VLAN IDs */
+
+       bool                multi_package;   /* Enable multiple packages   */
+       u32                 package_whitelist; /* Packages to configure    */
 };
 
 struct ncsi_cmd_arg {
@@ -341,6 +353,7 @@ extern spinlock_t ncsi_dev_lock;
        list_for_each_entry_rcu(nc, &np->channels, node)
 
 /* Resources */
+int ncsi_reset_dev(struct ncsi_dev *nd);
 void ncsi_start_channel_monitor(struct ncsi_channel *nc);
 void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
 struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
@@ -361,6 +374,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
 void ncsi_free_request(struct ncsi_request *nr);
 struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
 int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
+bool ncsi_channel_has_link(struct ncsi_channel *channel);
+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
+                         struct ncsi_channel *channel);
+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
+                          struct ncsi_package *np,
+                          struct ncsi_channel *disable,
+                          struct ncsi_channel *enable);
 
 /* Packet handlers */
 u32 ncsi_calculate_checksum(unsigned char *data, int len);
index 25e483e..26d67e2 100644 (file)
@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
                                struct ncsi_aen_pkt_hdr *h)
 {
-       struct ncsi_aen_lsc_pkt *lsc;
-       struct ncsi_channel *nc;
+       struct ncsi_channel *nc, *tmp;
        struct ncsi_channel_mode *ncm;
-       bool chained;
-       int state;
        unsigned long old_data, data;
+       struct ncsi_aen_lsc_pkt *lsc;
+       struct ncsi_package *np;
+       bool had_link, has_link;
        unsigned long flags;
+       bool chained;
+       int state;
 
        /* Find the NCSI channel */
        ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
        ncm->data[2] = data;
        ncm->data[4] = ntohl(lsc->oem_status);
 
+       had_link = !!(old_data & 0x1);
+       has_link = !!(data & 0x1);
+
        netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n",
                   nc->id, data & 0x1 ? "up" : "down");
 
@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
        state = nc->state;
        spin_unlock_irqrestore(&nc->lock, flags);
 
-       if (!((old_data ^ data) & 0x1) || chained)
-               return 0;
-       if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) &&
-           !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1)))
+       if (state == NCSI_CHANNEL_INACTIVE)
+               netdev_warn(ndp->ndev.dev,
+                           "NCSI: Inactive channel %u received AEN!\n",
+                           nc->id);
+
+       if ((had_link == has_link) || chained)
                return 0;
 
-       if (!(ndp->flags & NCSI_DEV_HWA) &&
-           state == NCSI_CHANNEL_ACTIVE)
-               ndp->flags |= NCSI_DEV_RESHUFFLE;
+       if (!ndp->multi_package && !nc->package->multi_channel) {
+               if (had_link) {
+                       ndp->flags |= NCSI_DEV_RESHUFFLE;
+                       ncsi_stop_channel_monitor(nc);
+                       spin_lock_irqsave(&ndp->lock, flags);
+                       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return ncsi_process_next_channel(ndp);
+               }
+               /* Configured channel came up */
+               return 0;
+       }
 
-       ncsi_stop_channel_monitor(nc);
-       spin_lock_irqsave(&ndp->lock, flags);
-       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-       spin_unlock_irqrestore(&ndp->lock, flags);
+       if (had_link) {
+               ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+               if (ncsi_channel_is_last(ndp, nc)) {
+                       /* No channels left, reconfigure */
+                       return ncsi_reset_dev(&ndp->ndev);
+               } else if (ncm->enable) {
+                       /* Need to failover Tx channel */
+                       ncsi_update_tx_channel(ndp, nc->package, nc, NULL);
+               }
+       } else if (has_link && nc->package->preferred_channel == nc) {
+               /* Return Tx to preferred channel */
+               ncsi_update_tx_channel(ndp, nc->package, NULL, nc);
+       } else if (has_link) {
+               NCSI_FOR_EACH_PACKAGE(ndp, np) {
+                       NCSI_FOR_EACH_CHANNEL(np, tmp) {
+                               /* Enable Tx on this channel if the current Tx
+                                * channel is down.
+                                */
+                               ncm = &tmp->modes[NCSI_MODE_TX_ENABLE];
+                               if (ncm->enable &&
+                                   !ncsi_channel_has_link(tmp)) {
+                                       ncsi_update_tx_channel(ndp, nc->package,
+                                                              tmp, nc);
+                                       break;
+                               }
+                       }
+               }
+       }
 
-       return ncsi_process_next_channel(ndp);
+       /* Leave configured channels active in a multi-channel scenario so
+        * AEN events are still received.
+        */
+       return 0;
 }
 
 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
index bfc43b2..31359d5 100644 (file)
 LIST_HEAD(ncsi_dev_list);
 DEFINE_SPINLOCK(ncsi_dev_lock);
 
+bool ncsi_channel_has_link(struct ncsi_channel *channel)
+{
+       return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
+}
+
+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
+                         struct ncsi_channel *channel)
+{
+       struct ncsi_package *np;
+       struct ncsi_channel *nc;
+
+       NCSI_FOR_EACH_PACKAGE(ndp, np)
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       if (nc == channel)
+                               continue;
+                       if (nc->state == NCSI_CHANNEL_ACTIVE &&
+                           ncsi_channel_has_link(nc))
+                               return false;
+               }
+
+       return true;
+}
+
 static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
 {
        struct ncsi_dev *nd = &ndp->ndev;
@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
                                continue;
                        }
 
-                       if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
+                       if (ncsi_channel_has_link(nc)) {
                                spin_unlock_irqrestore(&nc->lock, flags);
                                nd->link_up = 1;
                                goto report;
@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t)
        default:
                netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
                           nc->id);
-               if (!(ndp->flags & NCSI_DEV_HWA)) {
-                       ncsi_report_link(ndp, true);
-                       ndp->flags |= NCSI_DEV_RESHUFFLE;
-               }
+               ncsi_report_link(ndp, true);
+               ndp->flags |= NCSI_DEV_RESHUFFLE;
 
                ncsi_stop_channel_monitor(nc);
 
@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
        np->ndp = ndp;
        spin_lock_init(&np->lock);
        INIT_LIST_HEAD(&np->channels);
+       np->channel_whitelist = UINT_MAX;
 
        spin_lock_irqsave(&ndp->lock, flags);
        tmp = ncsi_find_package(ndp, id);
@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t)
 static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
 {
        struct ncsi_dev *nd = &ndp->ndev;
-       struct ncsi_package *np = ndp->active_package;
-       struct ncsi_channel *nc = ndp->active_channel;
+       struct ncsi_package *np;
+       struct ncsi_channel *nc, *tmp;
        struct ncsi_cmd_arg nca;
        unsigned long flags;
        int ret;
 
+       np = ndp->active_package;
+       nc = ndp->active_channel;
        nca.ndp = ndp;
        nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
        switch (nd->state) {
@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
                if (ret)
                        goto error;
 
+               NCSI_FOR_EACH_CHANNEL(np, tmp) {
+                       /* If there is another channel active on this package
+                        * do not deselect the package.
+                        */
+                       if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
+                               nd->state = ncsi_dev_state_suspend_done;
+                               break;
+                       }
+               }
                break;
        case ncsi_dev_state_suspend_deselect:
                ndp->pending_req_num = 1;
@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
                spin_lock_irqsave(&nc->lock, flags);
                nc->state = NCSI_CHANNEL_INACTIVE;
                spin_unlock_irqrestore(&nc->lock, flags);
-               ncsi_process_next_channel(ndp);
-
+               if (ndp->flags & NCSI_DEV_RESET)
+                       ncsi_reset_dev(nd);
+               else
+                       ncsi_process_next_channel(ndp);
                break;
        default:
                netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
@@ -675,12 +710,38 @@ static int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
        return ret;
 }
 
+static int ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca)
+{
+       union {
+               u8 data_u8[NCSI_OEM_MLX_CMD_GMA_LEN];
+               u32 data_u32[NCSI_OEM_MLX_CMD_GMA_LEN / sizeof(u32)];
+       } u;
+       int ret = 0;
+
+       nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN;
+
+       memset(&u, 0, sizeof(u));
+       u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
+       u.data_u8[5] = NCSI_OEM_MLX_CMD_GMA;
+       u.data_u8[6] = NCSI_OEM_MLX_CMD_GMA_PARAM;
+
+       nca->data = u.data_u8;
+
+       ret = ncsi_xmit_cmd(nca);
+       if (ret)
+               netdev_err(nca->ndp->ndev.dev,
+                          "NCSI: Failed to transmit cmd 0x%x during configure\n",
+                          nca->type);
+       return ret;
+}
+
 /* OEM Command handlers initialization */
 static struct ncsi_oem_gma_handler {
        unsigned int    mfr_id;
        int             (*handler)(struct ncsi_cmd_arg *nca);
 } ncsi_oem_gma_handlers[] = {
-       { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm }
+       { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm },
+       { NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx }
 };
 
 static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
@@ -717,13 +778,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
 
 #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
 
+/* Determine if a given channel from the channel_queue should be used for Tx */
+static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
+                              struct ncsi_channel *nc)
+{
+       struct ncsi_channel_mode *ncm;
+       struct ncsi_channel *channel;
+       struct ncsi_package *np;
+
+       /* Check if any other channel has Tx enabled; a channel may have already
+        * been configured and removed from the channel queue.
+        */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (!ndp->multi_package && np != nc->package)
+                       continue;
+               NCSI_FOR_EACH_CHANNEL(np, channel) {
+                       ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
+                       if (ncm->enable)
+                               return false;
+               }
+       }
+
+       /* This channel is the preferred channel and has link */
+       list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
+               np = channel->package;
+               if (np->preferred_channel &&
+                   ncsi_channel_has_link(np->preferred_channel)) {
+                       return np->preferred_channel == nc;
+               }
+       }
+
+       /* This channel has link */
+       if (ncsi_channel_has_link(nc))
+               return true;
+
+       list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
+               if (ncsi_channel_has_link(channel))
+                       return false;
+
+       /* No other channel has link; default to this one */
+       return true;
+}
+
+/* Change the active Tx channel in a multi-channel setup */
+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
+                          struct ncsi_package *package,
+                          struct ncsi_channel *disable,
+                          struct ncsi_channel *enable)
+{
+       struct ncsi_cmd_arg nca;
+       struct ncsi_channel *nc;
+       struct ncsi_package *np;
+       int ret = 0;
+
+       if (!package->multi_channel && !ndp->multi_package)
+               netdev_warn(ndp->ndev.dev,
+                           "NCSI: Trying to update Tx channel in single-channel mode\n");
+       nca.ndp = ndp;
+       nca.req_flags = 0;
+
+       /* Find current channel with Tx enabled */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (disable)
+                       break;
+               if (!ndp->multi_package && np != package)
+                       continue;
+
+               NCSI_FOR_EACH_CHANNEL(np, nc)
+                       if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
+                               disable = nc;
+                               break;
+                       }
+       }
+
+       /* Find a suitable channel for Tx */
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               if (enable)
+                       break;
+               if (!ndp->multi_package && np != package)
+                       continue;
+               if (!(ndp->package_whitelist & (0x1 << np->id)))
+                       continue;
+
+               if (np->preferred_channel &&
+                   ncsi_channel_has_link(np->preferred_channel)) {
+                       enable = np->preferred_channel;
+                       break;
+               }
+
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       if (!(np->channel_whitelist & 0x1 << nc->id))
+                               continue;
+                       if (nc->state != NCSI_CHANNEL_ACTIVE)
+                               continue;
+                       if (ncsi_channel_has_link(nc)) {
+                               enable = nc;
+                               break;
+                       }
+               }
+       }
+
+       if (disable == enable)
+               return -1;
+
+       if (!enable)
+               return -1;
+
+       if (disable) {
+               nca.channel = disable->id;
+               nca.package = disable->package->id;
+               nca.type = NCSI_PKT_CMD_DCNT;
+               ret = ncsi_xmit_cmd(&nca);
+               if (ret)
+                       netdev_err(ndp->ndev.dev,
+                                  "Error %d sending DCNT\n",
+                                  ret);
+       }
+
+       netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
+
+       nca.channel = enable->id;
+       nca.package = enable->package->id;
+       nca.type = NCSI_PKT_CMD_ECNT;
+       ret = ncsi_xmit_cmd(&nca);
+       if (ret)
+               netdev_err(ndp->ndev.dev,
+                          "Error %d sending ECNT\n",
+                          ret);
+
+       return ret;
+}
+
 static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
 {
-       struct ncsi_dev *nd = &ndp->ndev;
-       struct net_device *dev = nd->dev;
        struct ncsi_package *np = ndp->active_package;
        struct ncsi_channel *nc = ndp->active_channel;
        struct ncsi_channel *hot_nc = NULL;
+       struct ncsi_dev *nd = &ndp->ndev;
+       struct net_device *dev = nd->dev;
        struct ncsi_cmd_arg nca;
        unsigned char index;
        unsigned long flags;
@@ -845,20 +1037,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
                } else if (nd->state == ncsi_dev_state_config_ebf) {
                        nca.type = NCSI_PKT_CMD_EBF;
                        nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
-                       nd->state = ncsi_dev_state_config_ecnt;
+                       if (ncsi_channel_is_tx(ndp, nc))
+                               nd->state = ncsi_dev_state_config_ecnt;
+                       else
+                               nd->state = ncsi_dev_state_config_ec;
 #if IS_ENABLED(CONFIG_IPV6)
                        if (ndp->inet6_addr_num > 0 &&
                            (nc->caps[NCSI_CAP_GENERIC].cap &
                             NCSI_CAP_GENERIC_MC))
                                nd->state = ncsi_dev_state_config_egmf;
-                       else
-                               nd->state = ncsi_dev_state_config_ecnt;
                } else if (nd->state == ncsi_dev_state_config_egmf) {
                        nca.type = NCSI_PKT_CMD_EGMF;
                        nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
-                       nd->state = ncsi_dev_state_config_ecnt;
+                       if (ncsi_channel_is_tx(ndp, nc))
+                               nd->state = ncsi_dev_state_config_ecnt;
+                       else
+                               nd->state = ncsi_dev_state_config_ec;
 #endif /* CONFIG_IPV6 */
                } else if (nd->state == ncsi_dev_state_config_ecnt) {
+                       if (np->preferred_channel &&
+                           nc != np->preferred_channel)
+                               netdev_info(ndp->ndev.dev,
+                                           "NCSI: Tx failed over to channel %u\n",
+                                           nc->id);
                        nca.type = NCSI_PKT_CMD_ECNT;
                        nd->state = ncsi_dev_state_config_ec;
                } else if (nd->state == ncsi_dev_state_config_ec) {
@@ -889,6 +1090,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
                netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
                           nc->id);
                spin_lock_irqsave(&nc->lock, flags);
+               nc->state = NCSI_CHANNEL_ACTIVE;
+
+               if (ndp->flags & NCSI_DEV_RESET) {
+                       /* A reset event happened during config, start it now */
+                       nc->reconfigure_needed = false;
+                       spin_unlock_irqrestore(&nc->lock, flags);
+                       ncsi_reset_dev(nd);
+                       break;
+               }
+
                if (nc->reconfigure_needed) {
                        /* This channel's configuration has been updated
                         * part-way during the config state - start the
@@ -909,10 +1120,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
 
                if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
                        hot_nc = nc;
-                       nc->state = NCSI_CHANNEL_ACTIVE;
                } else {
                        hot_nc = NULL;
-                       nc->state = NCSI_CHANNEL_INACTIVE;
                        netdev_dbg(ndp->ndev.dev,
                                   "NCSI: channel %u link down after config\n",
                                   nc->id);
@@ -940,43 +1149,35 @@ error:
 
 static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
 {
-       struct ncsi_package *np, *force_package;
-       struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
+       struct ncsi_channel *nc, *found, *hot_nc;
        struct ncsi_channel_mode *ncm;
-       unsigned long flags;
+       unsigned long flags, cflags;
+       struct ncsi_package *np;
+       bool with_link;
 
        spin_lock_irqsave(&ndp->lock, flags);
        hot_nc = ndp->hot_channel;
-       force_channel = ndp->force_channel;
-       force_package = ndp->force_package;
        spin_unlock_irqrestore(&ndp->lock, flags);
 
-       /* Force a specific channel whether or not it has link if we have been
-        * configured to do so
-        */
-       if (force_package && force_channel) {
-               found = force_channel;
-               ncm = &found->modes[NCSI_MODE_LINK];
-               if (!(ncm->data[2] & 0x1))
-                       netdev_info(ndp->ndev.dev,
-                                   "NCSI: Channel %u forced, but it is link down\n",
-                                   found->id);
-               goto out;
-       }
-
-       /* The search is done once an inactive channel with up
-        * link is found.
+       /* By default the search is done once an inactive channel with up
+        * link is found, unless a preferred channel is set.
+        * If multi_package or multi_channel are configured all channels in the
+        * whitelist are added to the channel queue.
         */
        found = NULL;
+       with_link = false;
        NCSI_FOR_EACH_PACKAGE(ndp, np) {
-               if (ndp->force_package && np != ndp->force_package)
+               if (!(ndp->package_whitelist & (0x1 << np->id)))
                        continue;
                NCSI_FOR_EACH_CHANNEL(np, nc) {
-                       spin_lock_irqsave(&nc->lock, flags);
+                       if (!(np->channel_whitelist & (0x1 << nc->id)))
+                               continue;
+
+                       spin_lock_irqsave(&nc->lock, cflags);
 
                        if (!list_empty(&nc->link) ||
                            nc->state != NCSI_CHANNEL_INACTIVE) {
-                               spin_unlock_irqrestore(&nc->lock, flags);
+                               spin_unlock_irqrestore(&nc->lock, cflags);
                                continue;
                        }
 
@@ -988,32 +1189,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
 
                        ncm = &nc->modes[NCSI_MODE_LINK];
                        if (ncm->data[2] & 0x1) {
-                               spin_unlock_irqrestore(&nc->lock, flags);
                                found = nc;
-                               goto out;
+                               with_link = true;
                        }
 
-                       spin_unlock_irqrestore(&nc->lock, flags);
+                       /* If multi_channel is enabled configure all valid
+                        * channels whether or not they currently have link
+                        * so they will have AENs enabled.
+                        */
+                       if (with_link || np->multi_channel) {
+                               spin_lock_irqsave(&ndp->lock, flags);
+                               list_add_tail_rcu(&nc->link,
+                                                 &ndp->channel_queue);
+                               spin_unlock_irqrestore(&ndp->lock, flags);
+
+                               netdev_dbg(ndp->ndev.dev,
+                                          "NCSI: Channel %u added to queue (link %s)\n",
+                                          nc->id,
+                                          ncm->data[2] & 0x1 ? "up" : "down");
+                       }
+
+                       spin_unlock_irqrestore(&nc->lock, cflags);
+
+                       if (with_link && !np->multi_channel)
+                               break;
                }
+               if (with_link && !ndp->multi_package)
+                       break;
        }
 
-       if (!found) {
+       if (list_empty(&ndp->channel_queue) && found) {
+               netdev_info(ndp->ndev.dev,
+                           "NCSI: No channel with link found, configuring channel %u\n",
+                           found->id);
+               spin_lock_irqsave(&ndp->lock, flags);
+               list_add_tail_rcu(&found->link, &ndp->channel_queue);
+               spin_unlock_irqrestore(&ndp->lock, flags);
+       } else if (!found) {
                netdev_warn(ndp->ndev.dev,
-                           "NCSI: No channel found with link\n");
+                           "NCSI: No channel found to configure!\n");
                ncsi_report_link(ndp, true);
                return -ENODEV;
        }
 
-       ncm = &found->modes[NCSI_MODE_LINK];
-       netdev_dbg(ndp->ndev.dev,
-                  "NCSI: Channel %u added to queue (link %s)\n",
-                  found->id, ncm->data[2] & 0x1 ? "up" : "down");
-
-out:
-       spin_lock_irqsave(&ndp->lock, flags);
-       list_add_tail_rcu(&found->link, &ndp->channel_queue);
-       spin_unlock_irqrestore(&ndp->lock, flags);
-
        return ncsi_process_next_channel(ndp);
 }
 
@@ -1050,35 +1268,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
        return false;
 }
 
-static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
-{
-       struct ncsi_package *np;
-       struct ncsi_channel *nc;
-       unsigned long flags;
-
-       /* Move all available channels to processing queue */
-       spin_lock_irqsave(&ndp->lock, flags);
-       NCSI_FOR_EACH_PACKAGE(ndp, np) {
-               NCSI_FOR_EACH_CHANNEL(np, nc) {
-                       WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
-                                    !list_empty(&nc->link));
-                       ncsi_stop_channel_monitor(nc);
-                       list_add_tail_rcu(&nc->link, &ndp->channel_queue);
-               }
-       }
-       spin_unlock_irqrestore(&ndp->lock, flags);
-
-       /* We can have no channels in extremely case */
-       if (list_empty(&ndp->channel_queue)) {
-               netdev_err(ndp->ndev.dev,
-                          "NCSI: No available channels for HWA\n");
-               ncsi_report_link(ndp, false);
-               return -ENOENT;
-       }
-
-       return ncsi_process_next_channel(ndp);
-}
-
 static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
 {
        struct ncsi_dev *nd = &ndp->ndev;
@@ -1110,70 +1299,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
                nd->state = ncsi_dev_state_probe_package;
                break;
        case ncsi_dev_state_probe_package:
-               ndp->pending_req_num = 16;
+               ndp->pending_req_num = 1;
 
-               /* Select all possible packages */
                nca.type = NCSI_PKT_CMD_SP;
                nca.bytes[0] = 1;
+               nca.package = ndp->package_probe_id;
                nca.channel = NCSI_RESERVED_CHANNEL;
-               for (index = 0; index < 8; index++) {
-                       nca.package = index;
-                       ret = ncsi_xmit_cmd(&nca);
-                       if (ret)
-                               goto error;
-               }
-
-               /* Disable all possible packages */
-               nca.type = NCSI_PKT_CMD_DP;
-               for (index = 0; index < 8; index++) {
-                       nca.package = index;
-                       ret = ncsi_xmit_cmd(&nca);
-                       if (ret)
-                               goto error;
-               }
-
+               ret = ncsi_xmit_cmd(&nca);
+               if (ret)
+                       goto error;
                nd->state = ncsi_dev_state_probe_channel;
                break;
        case ncsi_dev_state_probe_channel:
-               if (!ndp->active_package)
-                       ndp->active_package = list_first_or_null_rcu(
-                               &ndp->packages, struct ncsi_package, node);
-               else if (list_is_last(&ndp->active_package->node,
-                                     &ndp->packages))
-                       ndp->active_package = NULL;
-               else
-                       ndp->active_package = list_next_entry(
-                               ndp->active_package, node);
-
-               /* All available packages and channels are enumerated. The
-                * enumeration happens for once when the NCSI interface is
-                * started. So we need continue to start the interface after
-                * the enumeration.
-                *
-                * We have to choose an active channel before configuring it.
-                * Note that we possibly don't have active channel in extreme
-                * situation.
-                */
+               ndp->active_package = ncsi_find_package(ndp,
+                                                       ndp->package_probe_id);
                if (!ndp->active_package) {
-                       ndp->flags |= NCSI_DEV_PROBED;
-                       if (ncsi_check_hwa(ndp))
-                               ncsi_enable_hwa(ndp);
-                       else
-                               ncsi_choose_active_channel(ndp);
-                       return;
+                       /* No response */
+                       nd->state = ncsi_dev_state_probe_dp;
+                       schedule_work(&ndp->work);
+                       break;
                }
-
-               /* Select the active package */
-               ndp->pending_req_num = 1;
-               nca.type = NCSI_PKT_CMD_SP;
-               nca.bytes[0] = 1;
-               nca.package = ndp->active_package->id;
-               nca.channel = NCSI_RESERVED_CHANNEL;
-               ret = ncsi_xmit_cmd(&nca);
-               if (ret)
-                       goto error;
-
                nd->state = ncsi_dev_state_probe_cis;
+               schedule_work(&ndp->work);
                break;
        case ncsi_dev_state_probe_cis:
                ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
@@ -1222,22 +1369,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
        case ncsi_dev_state_probe_dp:
                ndp->pending_req_num = 1;
 
-               /* Deselect the active package */
+               /* Deselect the current package */
                nca.type = NCSI_PKT_CMD_DP;
-               nca.package = ndp->active_package->id;
+               nca.package = ndp->package_probe_id;
                nca.channel = NCSI_RESERVED_CHANNEL;
                ret = ncsi_xmit_cmd(&nca);
                if (ret)
                        goto error;
 
-               /* Scan channels in next package */
-               nd->state = ncsi_dev_state_probe_channel;
+               /* Probe next package */
+               ndp->package_probe_id++;
+               if (ndp->package_probe_id >= 8) {
+                       /* Probe finished */
+                       ndp->flags |= NCSI_DEV_PROBED;
+                       break;
+               }
+               nd->state = ncsi_dev_state_probe_package;
+               ndp->active_package = NULL;
                break;
        default:
                netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
                            nd->state);
        }
 
+       if (ndp->flags & NCSI_DEV_PROBED) {
+               /* Check if all packages have HWA support */
+               ncsi_check_hwa(ndp);
+               ncsi_choose_active_channel(ndp);
+       }
+
        return;
 error:
        netdev_err(ndp->ndev.dev,
@@ -1556,6 +1716,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
        INIT_LIST_HEAD(&ndp->channel_queue);
        INIT_LIST_HEAD(&ndp->vlan_vids);
        INIT_WORK(&ndp->work, ncsi_dev_work);
+       ndp->package_whitelist = UINT_MAX;
 
        /* Initialize private NCSI device */
        spin_lock_init(&ndp->lock);
@@ -1592,26 +1753,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev);
 int ncsi_start_dev(struct ncsi_dev *nd)
 {
        struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
-       int ret;
 
        if (nd->state != ncsi_dev_state_registered &&
            nd->state != ncsi_dev_state_functional)
                return -ENOTTY;
 
        if (!(ndp->flags & NCSI_DEV_PROBED)) {
+               ndp->package_probe_id = 0;
                nd->state = ncsi_dev_state_probe;
                schedule_work(&ndp->work);
                return 0;
        }
 
-       if (ndp->flags & NCSI_DEV_HWA) {
-               netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n");
-               ret = ncsi_enable_hwa(ndp);
-       } else {
-               ret = ncsi_choose_active_channel(ndp);
-       }
-
-       return ret;
+       return ncsi_reset_dev(nd);
 }
 EXPORT_SYMBOL_GPL(ncsi_start_dev);
 
@@ -1624,7 +1778,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
        int old_state;
        unsigned long flags;
 
-       /* Stop the channel monitor and reset channel's state */
+       /* Stop the channel monitor on any active channels. Don't reset the
+        * channel state so we know which were active when ncsi_start_dev()
+        * is next called.
+        */
        NCSI_FOR_EACH_PACKAGE(ndp, np) {
                NCSI_FOR_EACH_CHANNEL(np, nc) {
                        ncsi_stop_channel_monitor(nc);
@@ -1632,7 +1789,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
                        spin_lock_irqsave(&nc->lock, flags);
                        chained = !list_empty(&nc->link);
                        old_state = nc->state;
-                       nc->state = NCSI_CHANNEL_INACTIVE;
                        spin_unlock_irqrestore(&nc->lock, flags);
 
                        WARN_ON_ONCE(chained ||
@@ -1645,6 +1801,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd)
 }
 EXPORT_SYMBOL_GPL(ncsi_stop_dev);
 
+int ncsi_reset_dev(struct ncsi_dev *nd)
+{
+       struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+       struct ncsi_channel *nc, *active, *tmp;
+       struct ncsi_package *np;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ndp->lock, flags);
+
+       if (!(ndp->flags & NCSI_DEV_RESET)) {
+               /* Haven't been called yet, check states */
+               switch (nd->state & ncsi_dev_state_major) {
+               case ncsi_dev_state_registered:
+               case ncsi_dev_state_probe:
+                       /* Not even probed yet - do nothing */
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               case ncsi_dev_state_suspend:
+               case ncsi_dev_state_config:
+                       /* Wait for the channel to finish its suspend/config
+                        * operation; once it finishes it will check for
+                        * NCSI_DEV_RESET and reset the state.
+                        */
+                       ndp->flags |= NCSI_DEV_RESET;
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               }
+       } else {
+               switch (nd->state) {
+               case ncsi_dev_state_suspend_done:
+               case ncsi_dev_state_config_done:
+               case ncsi_dev_state_functional:
+                       /* Ok */
+                       break;
+               default:
+                       /* Current reset operation happening */
+                       spin_unlock_irqrestore(&ndp->lock, flags);
+                       return 0;
+               }
+       }
+
+       if (!list_empty(&ndp->channel_queue)) {
+               /* Clear any channel queue we may have interrupted */
+               list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
+                       list_del_init(&nc->link);
+       }
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       active = NULL;
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               NCSI_FOR_EACH_CHANNEL(np, nc) {
+                       spin_lock_irqsave(&nc->lock, flags);
+
+                       if (nc->state == NCSI_CHANNEL_ACTIVE) {
+                               active = nc;
+                               nc->state = NCSI_CHANNEL_INVISIBLE;
+                               spin_unlock_irqrestore(&nc->lock, flags);
+                               ncsi_stop_channel_monitor(nc);
+                               break;
+                       }
+
+                       spin_unlock_irqrestore(&nc->lock, flags);
+               }
+               if (active)
+                       break;
+       }
+
+       if (!active) {
+               /* Done */
+               spin_lock_irqsave(&ndp->lock, flags);
+               ndp->flags &= ~NCSI_DEV_RESET;
+               spin_unlock_irqrestore(&ndp->lock, flags);
+               return ncsi_choose_active_channel(ndp);
+       }
+
+       spin_lock_irqsave(&ndp->lock, flags);
+       ndp->flags |= NCSI_DEV_RESET;
+       ndp->active_channel = active;
+       ndp->active_package = active->package;
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       nd->state = ncsi_dev_state_suspend;
+       schedule_work(&ndp->work);
+       return 0;
+}
+
 void ncsi_unregister_dev(struct ncsi_dev *nd)
 {
        struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
index 3331438..5d78244 100644 (file)
@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
        [NCSI_ATTR_PACKAGE_ID] =        { .type = NLA_U32 },
        [NCSI_ATTR_CHANNEL_ID] =        { .type = NLA_U32 },
        [NCSI_ATTR_DATA] =              { .type = NLA_BINARY, .len = 2048 },
+       [NCSI_ATTR_MULTI_FLAG] =        { .type = NLA_FLAG },
+       [NCSI_ATTR_PACKAGE_MASK] =      { .type = NLA_U32 },
+       [NCSI_ATTR_CHANNEL_MASK] =      { .type = NLA_U32 },
 };
 
 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
        nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
        if (nc->state == NCSI_CHANNEL_ACTIVE)
                nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
-       if (ndp->force_channel == nc)
+       if (nc == nc->package->preferred_channel)
                nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
 
        nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb,
                if (!pnest)
                        return -ENOMEM;
                nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
-               if (ndp->force_package == np)
+               if ((0x1 << np->id) == ndp->package_whitelist)
                        nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
                cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
                if (!cnest) {
@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
        package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
        package = NULL;
 
-       spin_lock_irqsave(&ndp->lock, flags);
-
        NCSI_FOR_EACH_PACKAGE(ndp, np)
                if (np->id == package_id)
                        package = np;
        if (!package) {
                /* The user has set a package that does not exist */
-               spin_unlock_irqrestore(&ndp->lock, flags);
                return -ERANGE;
        }
 
        channel = NULL;
-       if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
-               /* Allow any channel */
-               channel_id = NCSI_RESERVED_CHANNEL;
-       } else {
+       if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
                channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
                NCSI_FOR_EACH_CHANNEL(package, nc)
-                       if (nc->id == channel_id)
+                       if (nc->id == channel_id) {
                                channel = nc;
+                               break;
+                       }
+               if (!channel) {
+                       netdev_info(ndp->ndev.dev,
+                                   "NCSI: Channel %u does not exist!\n",
+                                   channel_id);
+                       return -ERANGE;
+               }
        }
 
-       if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
-               /* The user has set a channel that does not exist on this
-                * package
-                */
-               spin_unlock_irqrestore(&ndp->lock, flags);
-               netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
-                           channel_id);
-               return -ERANGE;
-       }
-
-       ndp->force_package = package;
-       ndp->force_channel = channel;
+       spin_lock_irqsave(&ndp->lock, flags);
+       ndp->package_whitelist = 0x1 << package->id;
+       ndp->multi_package = false;
        spin_unlock_irqrestore(&ndp->lock, flags);
 
-       netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
-                   package_id, channel_id,
-                   channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
+       spin_lock_irqsave(&package->lock, flags);
+       package->multi_channel = false;
+       if (channel) {
+               package->channel_whitelist = 0x1 << channel->id;
+               package->preferred_channel = channel;
+       } else {
+               /* Allow any channel */
+               package->channel_whitelist = UINT_MAX;
+               package->preferred_channel = NULL;
+       }
+       spin_unlock_irqrestore(&package->lock, flags);
+
+       if (channel)
+               netdev_info(ndp->ndev.dev,
+                           "Set package 0x%x, channel 0x%x as preferred\n",
+                           package_id, channel_id);
+       else
+               netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
+                           package_id);
 
-       /* Bounce the NCSI channel to set changes */
-       ncsi_stop_dev(&ndp->ndev);
-       ncsi_start_dev(&ndp->ndev);
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
 
        return 0;
 }
@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
 {
        struct ncsi_dev_priv *ndp;
+       struct ncsi_package *np;
        unsigned long flags;
 
        if (!info || !info->attrs)
@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
        if (!ndp)
                return -ENODEV;
 
-       /* Clear any override */
+       /* Reset any whitelists and disable multi mode */
        spin_lock_irqsave(&ndp->lock, flags);
-       ndp->force_package = NULL;
-       ndp->force_channel = NULL;
+       ndp->package_whitelist = UINT_MAX;
+       ndp->multi_package = false;
        spin_unlock_irqrestore(&ndp->lock, flags);
+
+       NCSI_FOR_EACH_PACKAGE(ndp, np) {
+               spin_lock_irqsave(&np->lock, flags);
+               np->multi_channel = false;
+               np->channel_whitelist = UINT_MAX;
+               np->preferred_channel = NULL;
+               spin_unlock_irqrestore(&np->lock, flags);
+       }
        netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
 
-       /* Bounce the NCSI channel to set changes */
-       ncsi_stop_dev(&ndp->ndev);
-       ncsi_start_dev(&ndp->ndev);
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
 
        return 0;
 }
@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev,
        return nlmsg_unicast(net->genl_sock, skb, snd_portid);
 }
 
+static int ncsi_set_package_mask_nl(struct sk_buff *msg,
+                                   struct genl_info *info)
+{
+       struct ncsi_dev_priv *ndp;
+       unsigned long flags;
+       int rc;
+
+       if (!info || !info->attrs)
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_IFINDEX])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
+               return -EINVAL;
+
+       ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+                              nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
+       if (!ndp)
+               return -ENODEV;
+
+       spin_lock_irqsave(&ndp->lock, flags);
+       if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
+               if (ndp->flags & NCSI_DEV_HWA) {
+                       ndp->multi_package = true;
+                       rc = 0;
+               } else {
+                       netdev_err(ndp->ndev.dev,
+                                  "NCSI: Can't use multiple packages without HWA\n");
+                       rc = -EPERM;
+               }
+       } else {
+               ndp->multi_package = false;
+               rc = 0;
+       }
+
+       if (!rc)
+               ndp->package_whitelist =
+                       nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
+       spin_unlock_irqrestore(&ndp->lock, flags);
+
+       if (!rc) {
+               /* Update channel configuration */
+               if (!(ndp->flags & NCSI_DEV_RESET))
+                       ncsi_reset_dev(&ndp->ndev);
+       }
+
+       return rc;
+}
+
+static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
+                                   struct genl_info *info)
+{
+       struct ncsi_package *np, *package;
+       struct ncsi_channel *nc, *channel;
+       u32 package_id, channel_id;
+       struct ncsi_dev_priv *ndp;
+       unsigned long flags;
+
+       if (!info || !info->attrs)
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_IFINDEX])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
+               return -EINVAL;
+
+       if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
+               return -EINVAL;
+
+       ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
+                              nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
+       if (!ndp)
+               return -ENODEV;
+
+       package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
+       package = NULL;
+       NCSI_FOR_EACH_PACKAGE(ndp, np)
+               if (np->id == package_id) {
+                       package = np;
+                       break;
+               }
+       if (!package)
+               return -ERANGE;
+
+       spin_lock_irqsave(&package->lock, flags);
+
+       channel = NULL;
+       if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
+               channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
+               NCSI_FOR_EACH_CHANNEL(np, nc)
+                       if (nc->id == channel_id) {
+                               channel = nc;
+                               break;
+                       }
+               if (!channel) {
+                       spin_unlock_irqrestore(&package->lock, flags);
+                       return -ERANGE;
+               }
+               netdev_dbg(ndp->ndev.dev,
+                          "NCSI: Channel %u set as preferred channel\n",
+                          channel->id);
+       }
+
+       package->channel_whitelist =
+               nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
+       if (package->channel_whitelist == 0)
+               netdev_dbg(ndp->ndev.dev,
+                          "NCSI: Package %u set to all channels disabled\n",
+                          package->id);
+
+       package->preferred_channel = channel;
+
+       if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
+               package->multi_channel = true;
+               netdev_info(ndp->ndev.dev,
+                           "NCSI: Multi-channel enabled on package %u\n",
+                           package_id);
+       } else {
+               package->multi_channel = false;
+       }
+
+       spin_unlock_irqrestore(&package->lock, flags);
+
+       /* Update channel configuration */
+       if (!(ndp->flags & NCSI_DEV_RESET))
+               ncsi_reset_dev(&ndp->ndev);
+
+       return 0;
+}
+
 static const struct genl_ops ncsi_ops[] = {
        {
                .cmd = NCSI_CMD_PKG_INFO,
@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = {
                .doit = ncsi_send_cmd_nl,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = NCSI_CMD_SET_PACKAGE_MASK,
+               .policy = ncsi_genl_policy,
+               .doit = ncsi_set_package_mask_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NCSI_CMD_SET_CHANNEL_MASK,
+               .policy = ncsi_genl_policy,
+               .doit = ncsi_set_channel_mask_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
 
 static struct genl_family ncsi_genl_family __ro_after_init = {
index 4d3f06b..2a6d83a 100644 (file)
@@ -165,6 +165,15 @@ struct ncsi_rsp_oem_pkt {
        unsigned char           data[];      /* Payload data      */
 };
 
+/* Mellanox Response Data */
+struct ncsi_rsp_oem_mlx_pkt {
+       unsigned char           cmd_rev;     /* Command Revision  */
+       unsigned char           cmd;         /* Command ID        */
+       unsigned char           param;       /* Parameter         */
+       unsigned char           optional;    /* Optional data     */
+       unsigned char           data[];      /* Data              */
+};
+
 /* Broadcom Response Data */
 struct ncsi_rsp_oem_bcm_pkt {
        unsigned char           ver;         /* Payload Version   */
index 77e07ba..dc07fcc 100644 (file)
@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
        if (!ncm->enable)
                return 0;
 
-       ncm->enable = 1;
+       ncm->enable = 0;
        return 0;
 }
 
@@ -611,6 +611,45 @@ static int ncsi_rsp_handler_snfc(struct ncsi_request *nr)
        return 0;
 }
 
+/* Response handler for Mellanox command Get Mac Address */
+static int ncsi_rsp_handler_oem_mlx_gma(struct ncsi_request *nr)
+{
+       struct ncsi_dev_priv *ndp = nr->ndp;
+       struct net_device *ndev = ndp->ndev.dev;
+       const struct net_device_ops *ops = ndev->netdev_ops;
+       struct ncsi_rsp_oem_pkt *rsp;
+       struct sockaddr saddr;
+       int ret = 0;
+
+       /* Get the response header */
+       rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
+
+       saddr.sa_family = ndev->type;
+       ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+       memcpy(saddr.sa_data, &rsp->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN);
+       ret = ops->ndo_set_mac_address(ndev, &saddr);
+       if (ret < 0)
+               netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n");
+
+       return ret;
+}
+
+/* Response handler for Mellanox card */
+static int ncsi_rsp_handler_oem_mlx(struct ncsi_request *nr)
+{
+       struct ncsi_rsp_oem_mlx_pkt *mlx;
+       struct ncsi_rsp_oem_pkt *rsp;
+
+       /* Get the response header */
+       rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
+       mlx = (struct ncsi_rsp_oem_mlx_pkt *)(rsp->data);
+
+       if (mlx->cmd == NCSI_OEM_MLX_CMD_GMA &&
+           mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM)
+               return ncsi_rsp_handler_oem_mlx_gma(nr);
+       return 0;
+}
+
 /* Response handler for Broadcom command Get Mac Address */
 static int ncsi_rsp_handler_oem_bcm_gma(struct ncsi_request *nr)
 {
@@ -655,7 +694,7 @@ static struct ncsi_rsp_oem_handler {
        unsigned int    mfr_id;
        int             (*handler)(struct ncsi_request *nr);
 } ncsi_rsp_oem_handlers[] = {
-       { NCSI_OEM_MFR_MLX_ID, NULL },
+       { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx },
        { NCSI_OEM_MFR_BCM_ID, ncsi_rsp_handler_oem_bcm }
 };
 
index 43041f0..1ce30ef 100644 (file)
@@ -1148,8 +1148,9 @@ static int nfqa_parse_bridge(struct nf_queue_entry *entry,
                if (!tb[NFQA_VLAN_TCI] || !tb[NFQA_VLAN_PROTO])
                        return -EINVAL;
 
-               entry->skb->vlan_tci = ntohs(nla_get_be16(tb[NFQA_VLAN_TCI]));
-               entry->skb->vlan_proto = nla_get_be16(tb[NFQA_VLAN_PROTO]);
+               __vlan_hwaccel_put_tag(entry->skb,
+                       nla_get_be16(tb[NFQA_VLAN_PROTO]),
+                       ntohs(nla_get_be16(tb[NFQA_VLAN_TCI])));
        }
 
        if (nfqa[NFQA_L2HDR]) {
index 85ae53d..e47ebbb 100644 (file)
@@ -301,7 +301,7 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
                key->eth.vlan.tpid = vlan->vlan_tpid;
        }
        return skb_vlan_push(skb, vlan->vlan_tpid,
-                            ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
+                            ntohs(vlan->vlan_tci) & ~VLAN_CFI_MASK);
 }
 
 /* 'src' is already properly masked. */
@@ -822,8 +822,10 @@ static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *sk
        __skb_dst_copy(skb, data->dst);
        *OVS_CB(skb) = data->cb;
        skb->inner_protocol = data->inner_protocol;
-       skb->vlan_tci = data->vlan_tci;
-       skb->vlan_proto = data->vlan_proto;
+       if (data->vlan_tci & VLAN_CFI_MASK)
+               __vlan_hwaccel_put_tag(skb, data->vlan_proto, data->vlan_tci & ~VLAN_CFI_MASK);
+       else
+               __vlan_hwaccel_clear_tag(skb);
 
        /* Reconstruct the MAC header.  */
        skb_push(skb, data->l2_len);
@@ -867,7 +869,10 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb,
        data->cb = *OVS_CB(skb);
        data->inner_protocol = skb->inner_protocol;
        data->network_offset = orig_network_offset;
-       data->vlan_tci = skb->vlan_tci;
+       if (skb_vlan_tag_present(skb))
+               data->vlan_tci = skb_vlan_tag_get(skb) | VLAN_CFI_MASK;
+       else
+               data->vlan_tci = 0;
        data->vlan_proto = skb->vlan_proto;
        data->mac_proto = mac_proto;
        data->l2_len = hlen;
index 35966da..57e0776 100644 (file)
@@ -325,7 +325,7 @@ static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh,
                return -ENOMEM;
 
        vh = (struct vlan_head *)skb->data;
-       key_vh->tci = vh->tci | htons(VLAN_TAG_PRESENT);
+       key_vh->tci = vh->tci | htons(VLAN_CFI_MASK);
        key_vh->tpid = vh->tpid;
 
        if (unlikely(untag_vlan)) {
@@ -358,7 +358,7 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
        int res;
 
        if (skb_vlan_tag_present(skb)) {
-               key->eth.vlan.tci = htons(skb->vlan_tci);
+               key->eth.vlan.tci = htons(skb->vlan_tci) | htons(VLAN_CFI_MASK);
                key->eth.vlan.tpid = skb->vlan_proto;
        } else {
                /* Parse outer vlan tag in the non-accelerated case. */
@@ -597,7 +597,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
                 * skb_vlan_pop(), which will later shift the ethertype into
                 * skb->protocol.
                 */
-               if (key->eth.cvlan.tci & htons(VLAN_TAG_PRESENT))
+               if (key->eth.cvlan.tci & htons(VLAN_CFI_MASK))
                        skb->protocol = key->eth.cvlan.tpid;
                else
                        skb->protocol = key->eth.type;
index c670dd2..ba01fc4 100644 (file)
@@ -60,7 +60,7 @@ struct ovs_tunnel_info {
 
 struct vlan_head {
        __be16 tpid; /* Vlan type. Generally 802.1q or 802.1ad.*/
-       __be16 tci;  /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
+       __be16 tci;  /* 0 if no VLAN, VLAN_CFI_MASK set otherwise. */
 };
 
 #define OVS_SW_FLOW_KEY_METADATA_SIZE                  \
index 865ecef..435a4bd 100644 (file)
@@ -990,9 +990,9 @@ static int validate_vlan_from_nlattrs(const struct sw_flow_match *match,
        if (a[OVS_KEY_ATTR_VLAN])
                tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
 
-       if (!(tci & htons(VLAN_TAG_PRESENT))) {
+       if (!(tci & htons(VLAN_CFI_MASK))) {
                if (tci) {
-                       OVS_NLERR(log, "%s TCI does not have VLAN_TAG_PRESENT bit set.",
+                       OVS_NLERR(log, "%s TCI does not have VLAN_CFI_MASK bit set.",
                                  (inner) ? "C-VLAN" : "VLAN");
                        return -EINVAL;
                } else if (nla_len(a[OVS_KEY_ATTR_ENCAP])) {
@@ -1013,9 +1013,9 @@ static int validate_vlan_mask_from_nlattrs(const struct sw_flow_match *match,
        __be16 tci = 0;
        __be16 tpid = 0;
        bool encap_valid = !!(match->key->eth.vlan.tci &
-                             htons(VLAN_TAG_PRESENT));
+                             htons(VLAN_CFI_MASK));
        bool i_encap_valid = !!(match->key->eth.cvlan.tci &
-                               htons(VLAN_TAG_PRESENT));
+                               htons(VLAN_CFI_MASK));
 
        if (!(key_attrs & (1 << OVS_KEY_ATTR_ENCAP))) {
                /* Not a VLAN. */
@@ -1039,8 +1039,8 @@ static int validate_vlan_mask_from_nlattrs(const struct sw_flow_match *match,
                          (inner) ? "C-VLAN" : "VLAN", ntohs(tpid));
                return -EINVAL;
        }
-       if (!(tci & htons(VLAN_TAG_PRESENT))) {
-               OVS_NLERR(log, "%s TCI mask does not have exact match for VLAN_TAG_PRESENT bit.",
+       if (!(tci & htons(VLAN_CFI_MASK))) {
+               OVS_NLERR(log, "%s TCI mask does not have exact match for VLAN_CFI_MASK bit.",
                          (inner) ? "C-VLAN" : "VLAN");
                return -EINVAL;
        }
@@ -1095,7 +1095,7 @@ static int parse_vlan_from_nlattrs(struct sw_flow_match *match,
        if (err)
                return err;
 
-       encap_valid = !!(match->key->eth.vlan.tci & htons(VLAN_TAG_PRESENT));
+       encap_valid = !!(match->key->eth.vlan.tci & htons(VLAN_CFI_MASK));
        if (encap_valid) {
                err = __parse_vlan_from_nlattrs(match, key_attrs, true, a,
                                                is_mask, log);
@@ -2943,7 +2943,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                        vlan = nla_data(a);
                        if (!eth_type_vlan(vlan->vlan_tpid))
                                return -EINVAL;
-                       if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
+                       if (!(vlan->vlan_tci & htons(VLAN_CFI_MASK)))
                                return -EINVAL;
                        vlan_tci = vlan->vlan_tci;
                        break;
@@ -2959,7 +2959,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                        /* Prohibit push MPLS other than to a white list
                         * for packets that have a known tag order.
                         */
-                       if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
+                       if (vlan_tci & htons(VLAN_CFI_MASK) ||
                            (eth_type != htons(ETH_P_IP) &&
                             eth_type != htons(ETH_P_IPV6) &&
                             eth_type != htons(ETH_P_ARP) &&
@@ -2971,7 +2971,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                }
 
                case OVS_ACTION_ATTR_POP_MPLS:
-                       if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
+                       if (vlan_tci & htons(VLAN_CFI_MASK) ||
                            !eth_p_mpls(eth_type))
                                return -EINVAL;
 
@@ -3036,7 +3036,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                case OVS_ACTION_ATTR_POP_ETH:
                        if (mac_proto != MAC_PROTO_ETHERNET)
                                return -EINVAL;
-                       if (vlan_tci & htons(VLAN_TAG_PRESENT))
+                       if (vlan_tci & htons(VLAN_CFI_MASK))
                                return -EINVAL;
                        mac_proto = MAC_PROTO_NONE;
                        break;
index 5aaf3ba..acb6077 100644 (file)
@@ -93,7 +93,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index 0e72d95..c38a624 100644 (file)
@@ -68,7 +68,7 @@ static struct vport *gre_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index 2e5e7a4..9bec22e 100644 (file)
@@ -84,7 +84,6 @@ static struct net_device *get_dpdev(const struct datapath *dp)
        struct vport *local;
 
        local = ovs_vport_ovsl(dp, OVSP_LOCAL);
-       BUG_ON(!local);
        return local->dev;
 }
 
index 7e6301b..8f16f11 100644 (file)
@@ -131,7 +131,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
                return ERR_CAST(dev);
        }
 
-       err = dev_change_flags(dev, dev->flags | IFF_UP);
+       err = dev_change_flags(dev, dev->flags | IFF_UP, NULL);
        if (err < 0) {
                rtnl_delete_link(dev);
                rtnl_unlock();
index 4cca8f2..c3b90fa 100644 (file)
@@ -210,9 +210,9 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
        struct tcf_tunnel_key *t;
        bool exists = false;
        __be16 dst_port = 0;
+       __be64 key_id = 0;
        int opts_len = 0;
-       __be64 key_id;
-       __be16 flags;
+       __be16 flags = 0;
        u8 tos, ttl;
        int ret = 0;
        int err;
@@ -246,15 +246,15 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
        case TCA_TUNNEL_KEY_ACT_RELEASE:
                break;
        case TCA_TUNNEL_KEY_ACT_SET:
-               if (!tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
-                       NL_SET_ERR_MSG(extack, "Missing tunnel key id");
-                       ret = -EINVAL;
-                       goto err_out;
-               }
+               if (tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) {
+                       __be32 key32;
 
-               key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]));
+                       key32 = nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
+                       key_id = key32_to_tunnel_id(key32);
+                       flags = TUNNEL_KEY;
+               }
 
-               flags = TUNNEL_KEY | TUNNEL_CSUM;
+               flags |= TUNNEL_CSUM;
                if (tb[TCA_TUNNEL_KEY_NO_CSUM] &&
                    nla_get_u8(tb[TCA_TUNNEL_KEY_NO_CSUM]))
                        flags &= ~TUNNEL_CSUM;
@@ -508,10 +508,13 @@ static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
                struct ip_tunnel_key *key = &info->key;
                __be32 key_id = tunnel_id_to_key32(key->tun_id);
 
-               if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) ||
+               if (((key->tun_flags & TUNNEL_KEY) &&
+                    nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id)) ||
                    tunnel_key_dump_addresses(skb,
                                              &params->tcft_enc_metadata->u.tun_info) ||
-                   nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) ||
+                   (key->tp_dst &&
+                     nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT,
+                                  key->tp_dst)) ||
                    nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
                               !(key->tun_flags & TUNNEL_CSUM)) ||
                    tunnel_key_opts_dump(skb, info))
index ba677d5..93fdaf7 100644 (file)
@@ -63,7 +63,7 @@ static int tcf_vlan_act(struct sk_buff *skb, const struct tc_action *a,
                /* extract existing tag (and guarantee no hw-accel tag) */
                if (skb_vlan_tag_present(skb)) {
                        tci = skb_vlan_tag_get(skb);
-                       skb->vlan_tci = 0;
+                       __vlan_hwaccel_clear_tag(skb);
                } else {
                        /* in-payload vlan tag, pop it */
                        err = __skb_vlan_pop(skb, &tci);
index f427a1e..d92f44a 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kmod.h>
 #include <linux/slab.h>
 #include <linux/idr.h>
+#include <linux/rhashtable.h>
 #include <net/net_namespace.h>
 #include <net/sock.h>
 #include <net/netlink.h>
@@ -365,6 +366,245 @@ static void tcf_chain_flush(struct tcf_chain *chain)
        }
 }
 
+static struct tcf_block *tc_dev_ingress_block(struct net_device *dev)
+{
+       const struct Qdisc_class_ops *cops;
+       struct Qdisc *qdisc;
+
+       if (!dev_ingress_queue(dev))
+               return NULL;
+
+       qdisc = dev_ingress_queue(dev)->qdisc_sleeping;
+       if (!qdisc)
+               return NULL;
+
+       cops = qdisc->ops->cl_ops;
+       if (!cops)
+               return NULL;
+
+       if (!cops->tcf_block)
+               return NULL;
+
+       return cops->tcf_block(qdisc, TC_H_MIN_INGRESS, NULL);
+}
+
+static struct rhashtable indr_setup_block_ht;
+
+struct tc_indr_block_dev {
+       struct rhash_head ht_node;
+       struct net_device *dev;
+       unsigned int refcnt;
+       struct list_head cb_list;
+       struct tcf_block *block;
+};
+
+struct tc_indr_block_cb {
+       struct list_head list;
+       void *cb_priv;
+       tc_indr_block_bind_cb_t *cb;
+       void *cb_ident;
+};
+
+static const struct rhashtable_params tc_indr_setup_block_ht_params = {
+       .key_offset     = offsetof(struct tc_indr_block_dev, dev),
+       .head_offset    = offsetof(struct tc_indr_block_dev, ht_node),
+       .key_len        = sizeof(struct net_device *),
+};
+
+static struct tc_indr_block_dev *
+tc_indr_block_dev_lookup(struct net_device *dev)
+{
+       return rhashtable_lookup_fast(&indr_setup_block_ht, &dev,
+                                     tc_indr_setup_block_ht_params);
+}
+
+static struct tc_indr_block_dev *tc_indr_block_dev_get(struct net_device *dev)
+{
+       struct tc_indr_block_dev *indr_dev;
+
+       indr_dev = tc_indr_block_dev_lookup(dev);
+       if (indr_dev)
+               goto inc_ref;
+
+       indr_dev = kzalloc(sizeof(*indr_dev), GFP_KERNEL);
+       if (!indr_dev)
+               return NULL;
+
+       INIT_LIST_HEAD(&indr_dev->cb_list);
+       indr_dev->dev = dev;
+       indr_dev->block = tc_dev_ingress_block(dev);
+       if (rhashtable_insert_fast(&indr_setup_block_ht, &indr_dev->ht_node,
+                                  tc_indr_setup_block_ht_params)) {
+               kfree(indr_dev);
+               return NULL;
+       }
+
+inc_ref:
+       indr_dev->refcnt++;
+       return indr_dev;
+}
+
+static void tc_indr_block_dev_put(struct tc_indr_block_dev *indr_dev)
+{
+       if (--indr_dev->refcnt)
+               return;
+
+       rhashtable_remove_fast(&indr_setup_block_ht, &indr_dev->ht_node,
+                              tc_indr_setup_block_ht_params);
+       kfree(indr_dev);
+}
+
+static struct tc_indr_block_cb *
+tc_indr_block_cb_lookup(struct tc_indr_block_dev *indr_dev,
+                       tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       struct tc_indr_block_cb *indr_block_cb;
+
+       list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list)
+               if (indr_block_cb->cb == cb &&
+                   indr_block_cb->cb_ident == cb_ident)
+                       return indr_block_cb;
+       return NULL;
+}
+
+static struct tc_indr_block_cb *
+tc_indr_block_cb_add(struct tc_indr_block_dev *indr_dev, void *cb_priv,
+                    tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       struct tc_indr_block_cb *indr_block_cb;
+
+       indr_block_cb = tc_indr_block_cb_lookup(indr_dev, cb, cb_ident);
+       if (indr_block_cb)
+               return ERR_PTR(-EEXIST);
+
+       indr_block_cb = kzalloc(sizeof(*indr_block_cb), GFP_KERNEL);
+       if (!indr_block_cb)
+               return ERR_PTR(-ENOMEM);
+
+       indr_block_cb->cb_priv = cb_priv;
+       indr_block_cb->cb = cb;
+       indr_block_cb->cb_ident = cb_ident;
+       list_add(&indr_block_cb->list, &indr_dev->cb_list);
+
+       return indr_block_cb;
+}
+
+static void tc_indr_block_cb_del(struct tc_indr_block_cb *indr_block_cb)
+{
+       list_del(&indr_block_cb->list);
+       kfree(indr_block_cb);
+}
+
+static void tc_indr_block_ing_cmd(struct tc_indr_block_dev *indr_dev,
+                                 struct tc_indr_block_cb *indr_block_cb,
+                                 enum tc_block_command command)
+{
+       struct tc_block_offload bo = {
+               .command        = command,
+               .binder_type    = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS,
+               .block          = indr_dev->block,
+       };
+
+       if (!indr_dev->block)
+               return;
+
+       indr_block_cb->cb(indr_dev->dev, indr_block_cb->cb_priv, TC_SETUP_BLOCK,
+                         &bo);
+}
+
+int __tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                               tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       struct tc_indr_block_cb *indr_block_cb;
+       struct tc_indr_block_dev *indr_dev;
+       int err;
+
+       indr_dev = tc_indr_block_dev_get(dev);
+       if (!indr_dev)
+               return -ENOMEM;
+
+       indr_block_cb = tc_indr_block_cb_add(indr_dev, cb_priv, cb, cb_ident);
+       err = PTR_ERR_OR_ZERO(indr_block_cb);
+       if (err)
+               goto err_dev_put;
+
+       tc_indr_block_ing_cmd(indr_dev, indr_block_cb, TC_BLOCK_BIND);
+       return 0;
+
+err_dev_put:
+       tc_indr_block_dev_put(indr_dev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(__tc_indr_block_cb_register);
+
+int tc_indr_block_cb_register(struct net_device *dev, void *cb_priv,
+                             tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       int err;
+
+       rtnl_lock();
+       err = __tc_indr_block_cb_register(dev, cb_priv, cb, cb_ident);
+       rtnl_unlock();
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(tc_indr_block_cb_register);
+
+void __tc_indr_block_cb_unregister(struct net_device *dev,
+                                  tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       struct tc_indr_block_cb *indr_block_cb;
+       struct tc_indr_block_dev *indr_dev;
+
+       indr_dev = tc_indr_block_dev_lookup(dev);
+       if (!indr_dev)
+               return;
+
+       indr_block_cb = tc_indr_block_cb_lookup(indr_dev, cb, cb_ident);
+       if (!indr_block_cb)
+               return;
+
+       /* Send unbind message if required to free any block cbs. */
+       tc_indr_block_ing_cmd(indr_dev, indr_block_cb, TC_BLOCK_UNBIND);
+       tc_indr_block_cb_del(indr_block_cb);
+       tc_indr_block_dev_put(indr_dev);
+}
+EXPORT_SYMBOL_GPL(__tc_indr_block_cb_unregister);
+
+void tc_indr_block_cb_unregister(struct net_device *dev,
+                                tc_indr_block_bind_cb_t *cb, void *cb_ident)
+{
+       rtnl_lock();
+       __tc_indr_block_cb_unregister(dev, cb, cb_ident);
+       rtnl_unlock();
+}
+EXPORT_SYMBOL_GPL(tc_indr_block_cb_unregister);
+
+static void tc_indr_block_call(struct tcf_block *block, struct net_device *dev,
+                              struct tcf_block_ext_info *ei,
+                              enum tc_block_command command,
+                              struct netlink_ext_ack *extack)
+{
+       struct tc_indr_block_cb *indr_block_cb;
+       struct tc_indr_block_dev *indr_dev;
+       struct tc_block_offload bo = {
+               .command        = command,
+               .binder_type    = ei->binder_type,
+               .block          = block,
+               .extack         = extack,
+       };
+
+       indr_dev = tc_indr_block_dev_lookup(dev);
+       if (!indr_dev)
+               return;
+
+       indr_dev->block = command == TC_BLOCK_BIND ? block : NULL;
+
+       list_for_each_entry(indr_block_cb, &indr_dev->cb_list, list)
+               indr_block_cb->cb(dev, indr_block_cb->cb_priv, TC_SETUP_BLOCK,
+                                 &bo);
+}
+
 static bool tcf_block_offload_in_use(struct tcf_block *block)
 {
        return block->offloadcnt;
@@ -406,12 +646,17 @@ static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
        err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_BIND, extack);
        if (err == -EOPNOTSUPP)
                goto no_offload_dev_inc;
-       return err;
+       if (err)
+               return err;
+
+       tc_indr_block_call(block, dev, ei, TC_BLOCK_BIND, extack);
+       return 0;
 
 no_offload_dev_inc:
        if (tcf_block_offload_in_use(block))
                return -EOPNOTSUPP;
        block->nooffloaddevcnt++;
+       tc_indr_block_call(block, dev, ei, TC_BLOCK_BIND, extack);
        return 0;
 }
 
@@ -421,6 +666,8 @@ static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
        struct net_device *dev = q->dev_queue->dev;
        int err;
 
+       tc_indr_block_call(block, dev, ei, TC_BLOCK_UNBIND, NULL);
+
        if (!dev->netdev_ops->ndo_setup_tc)
                goto no_offload_dev_dec;
        err = tcf_block_offload_cmd(block, dev, ei, TC_BLOCK_UNBIND, NULL);
@@ -2355,6 +2602,11 @@ static int __init tc_filter_init(void)
        if (err)
                goto err_register_pernet_subsys;
 
+       err = rhashtable_init(&indr_setup_block_ht,
+                             &tc_indr_setup_block_ht_params);
+       if (err)
+               goto err_rhash_setup_block_ht;
+
        rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0);
        rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
@@ -2366,6 +2618,8 @@ static int __init tc_filter_init(void)
 
        return 0;
 
+err_rhash_setup_block_ht:
+       unregister_pernet_subsys(&tcf_net_ops);
 err_register_pernet_subsys:
        destroy_workqueue(tc_filter_wq);
        return err;
index 71312d7..544811d 100644 (file)
@@ -55,6 +55,8 @@ struct fl_flow_key {
        struct flow_dissector_key_ip ip;
        struct flow_dissector_key_ip enc_ip;
        struct flow_dissector_key_enc_opts enc_opts;
+       struct flow_dissector_key_ports tp_min;
+       struct flow_dissector_key_ports tp_max;
 } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 
 struct fl_flow_mask_range {
@@ -65,6 +67,7 @@ struct fl_flow_mask_range {
 struct fl_flow_mask {
        struct fl_flow_key key;
        struct fl_flow_mask_range range;
+       u32 flags;
        struct rhash_head ht_node;
        struct rhashtable ht;
        struct rhashtable_params filter_ht_params;
@@ -179,13 +182,89 @@ static void fl_clear_masked_range(struct fl_flow_key *key,
        memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask));
 }
 
-static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
-                                      struct fl_flow_key *mkey)
+static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
+                                 struct fl_flow_key *key,
+                                 struct fl_flow_key *mkey)
+{
+       __be16 min_mask, max_mask, min_val, max_val;
+
+       min_mask = htons(filter->mask->key.tp_min.dst);
+       max_mask = htons(filter->mask->key.tp_max.dst);
+       min_val = htons(filter->key.tp_min.dst);
+       max_val = htons(filter->key.tp_max.dst);
+
+       if (min_mask && max_mask) {
+               if (htons(key->tp.dst) < min_val ||
+                   htons(key->tp.dst) > max_val)
+                       return false;
+
+               /* skb does not have min and max values */
+               mkey->tp_min.dst = filter->mkey.tp_min.dst;
+               mkey->tp_max.dst = filter->mkey.tp_max.dst;
+       }
+       return true;
+}
+
+static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
+                                 struct fl_flow_key *key,
+                                 struct fl_flow_key *mkey)
+{
+       __be16 min_mask, max_mask, min_val, max_val;
+
+       min_mask = htons(filter->mask->key.tp_min.src);
+       max_mask = htons(filter->mask->key.tp_max.src);
+       min_val = htons(filter->key.tp_min.src);
+       max_val = htons(filter->key.tp_max.src);
+
+       if (min_mask && max_mask) {
+               if (htons(key->tp.src) < min_val ||
+                   htons(key->tp.src) > max_val)
+                       return false;
+
+               /* skb does not have min and max values */
+               mkey->tp_min.src = filter->mkey.tp_min.src;
+               mkey->tp_max.src = filter->mkey.tp_max.src;
+       }
+       return true;
+}
+
+static struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask,
+                                        struct fl_flow_key *mkey)
 {
        return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask),
                                      mask->filter_ht_params);
 }
 
+static struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask,
+                                            struct fl_flow_key *mkey,
+                                            struct fl_flow_key *key)
+{
+       struct cls_fl_filter *filter, *f;
+
+       list_for_each_entry_rcu(filter, &mask->filters, list) {
+               if (!fl_range_port_dst_cmp(filter, key, mkey))
+                       continue;
+
+               if (!fl_range_port_src_cmp(filter, key, mkey))
+                       continue;
+
+               f = __fl_lookup(mask, mkey);
+               if (f)
+                       return f;
+       }
+       return NULL;
+}
+
+static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
+                                      struct fl_flow_key *mkey,
+                                      struct fl_flow_key *key)
+{
+       if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE))
+               return fl_lookup_range(mask, mkey, key);
+
+       return __fl_lookup(mask, mkey);
+}
+
 static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                       struct tcf_result *res)
 {
@@ -208,7 +287,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
 
                fl_set_masked_key(&skb_mkey, &skb_key, mask);
 
-               f = fl_lookup(mask, &skb_mkey);
+               f = fl_lookup(mask, &skb_mkey, &skb_key);
                if (f && !tc_skip_sw(f->flags)) {
                        *res = f->res;
                        return tcf_exts_exec(skb, &f->exts, res);
@@ -514,6 +593,31 @@ static void fl_set_key_val(struct nlattr **tb,
                memcpy(mask, nla_data(tb[mask_type]), len);
 }
 
+static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
+                                struct fl_flow_key *mask)
+{
+       fl_set_key_val(tb, &key->tp_min.dst,
+                      TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst));
+       fl_set_key_val(tb, &key->tp_max.dst,
+                      TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst));
+       fl_set_key_val(tb, &key->tp_min.src,
+                      TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src));
+       fl_set_key_val(tb, &key->tp_max.src,
+                      TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src,
+                      TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src));
+
+       if ((mask->tp_min.dst && mask->tp_max.dst &&
+            htons(key->tp_max.dst) <= htons(key->tp_min.dst)) ||
+            (mask->tp_min.src && mask->tp_max.src &&
+             htons(key->tp_max.src) <= htons(key->tp_min.src)))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int fl_set_key_mpls(struct nlattr **tb,
                           struct flow_dissector_key_mpls *key_val,
                           struct flow_dissector_key_mpls *key_mask)
@@ -921,6 +1025,14 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
                               sizeof(key->arp.tha));
        }
 
+       if (key->basic.ip_proto == IPPROTO_TCP ||
+           key->basic.ip_proto == IPPROTO_UDP ||
+           key->basic.ip_proto == IPPROTO_SCTP) {
+               ret = fl_set_key_port_range(tb, key, mask);
+               if (ret)
+                       return ret;
+       }
+
        if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] ||
            tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) {
                key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
@@ -1038,8 +1150,9 @@ static void fl_init_dissector(struct flow_dissector *dissector,
                             FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
                             FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
-       FL_KEY_SET_IF_MASKED(mask, keys, cnt,
-                            FLOW_DISSECTOR_KEY_PORTS, tp);
+       if (FL_KEY_IS_MASKED(mask, tp) ||
+           FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max))
+               FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
                             FLOW_DISSECTOR_KEY_IP, ip);
        FL_KEY_SET_IF_MASKED(mask, keys, cnt,
@@ -1086,6 +1199,10 @@ static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
 
        fl_mask_copy(newmask, mask);
 
+       if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) ||
+           (newmask->key.tp_min.src && newmask->key.tp_max.src))
+               newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
+
        err = fl_init_mask_hashtable(newmask);
        if (err)
                goto errout_free;
@@ -1238,7 +1355,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
        if (err)
                goto errout_idr;
 
-       if (!fold && fl_lookup(fnew->mask, &fnew->mkey)) {
+       if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
                err = -EEXIST;
                goto errout_mask;
        }
@@ -1473,6 +1590,26 @@ static int fl_dump_key_val(struct sk_buff *skb,
        return 0;
 }
 
+static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
+                                 struct fl_flow_key *mask)
+{
+       if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN,
+                           &mask->tp_min.dst, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_min.dst)) ||
+           fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX,
+                           &mask->tp_max.dst, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_max.dst)) ||
+           fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN,
+                           &mask->tp_min.src, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_min.src)) ||
+           fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX,
+                           &mask->tp_max.src, TCA_FLOWER_UNSPEC,
+                           sizeof(key->tp_max.src)))
+               return -1;
+
+       return 0;
+}
+
 static int fl_dump_key_mpls(struct sk_buff *skb,
                            struct flow_dissector_key_mpls *mpls_key,
                            struct flow_dissector_key_mpls *mpls_mask)
@@ -1809,6 +1946,12 @@ static int fl_dump_key(struct sk_buff *skb, struct net *net,
                                  sizeof(key->arp.tha))))
                goto nla_put_failure;
 
+       if ((key->basic.ip_proto == IPPROTO_TCP ||
+            key->basic.ip_proto == IPPROTO_UDP ||
+            key->basic.ip_proto == IPPROTO_SCTP) &&
+            fl_dump_key_port_range(skb, key, mask))
+               goto nla_put_failure;
+
        if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
            (fl_dump_key_val(skb, &key->enc_ipv4.src,
                            TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src,
index 4b28fd4..4c54bc4 100644 (file)
@@ -558,6 +558,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
        cls_u32.knode.mask = 0;
 #endif
        cls_u32.knode.sel = &n->sel;
+       cls_u32.knode.res = &n->res;
        cls_u32.knode.exts = &n->exts;
        if (n->ht_down)
                cls_u32.knode.link_handle = ht->handle;
@@ -1206,6 +1207,7 @@ static int u32_reoffload_knode(struct tcf_proto *tp, struct tc_u_knode *n,
                cls_u32.knode.mask = 0;
 #endif
                cls_u32.knode.sel = &n->sel;
+               cls_u32.knode.res = &n->res;
                cls_u32.knode.exts = &n->exts;
                if (n->ht_down)
                        cls_u32.knode.link_handle = ht->handle;
index ca3b0f4..9c88cec 100644 (file)
@@ -810,6 +810,71 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n,
 }
 EXPORT_SYMBOL(qdisc_tree_reduce_backlog);
 
+int qdisc_offload_dump_helper(struct Qdisc *sch, enum tc_setup_type type,
+                             void *type_data)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       int err;
+
+       sch->flags &= ~TCQ_F_OFFLOADED;
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return 0;
+
+       err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data);
+       if (err == -EOPNOTSUPP)
+               return 0;
+
+       if (!err)
+               sch->flags |= TCQ_F_OFFLOADED;
+
+       return err;
+}
+EXPORT_SYMBOL(qdisc_offload_dump_helper);
+
+void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch,
+                               struct Qdisc *new, struct Qdisc *old,
+                               enum tc_setup_type type, void *type_data,
+                               struct netlink_ext_ack *extack)
+{
+       bool any_qdisc_is_offloaded;
+       int err;
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data);
+
+       /* Don't report error if the graft is part of destroy operation. */
+       if (!err || !new || new == &noop_qdisc)
+               return;
+
+       /* Don't report error if the parent, the old child and the new
+        * one are not offloaded.
+        */
+       any_qdisc_is_offloaded = new->flags & TCQ_F_OFFLOADED;
+       any_qdisc_is_offloaded |= sch && sch->flags & TCQ_F_OFFLOADED;
+       any_qdisc_is_offloaded |= old && old->flags & TCQ_F_OFFLOADED;
+
+       if (any_qdisc_is_offloaded)
+               NL_SET_ERR_MSG(extack, "Offloading graft operation failed.");
+}
+EXPORT_SYMBOL(qdisc_offload_graft_helper);
+
+static void qdisc_offload_graft_root(struct net_device *dev,
+                                    struct Qdisc *new, struct Qdisc *old,
+                                    struct netlink_ext_ack *extack)
+{
+       struct tc_root_qopt_offload graft_offload = {
+               .command        = TC_ROOT_GRAFT,
+               .handle         = new ? new->handle : 0,
+               .ingress        = (new && new->flags & TCQ_F_INGRESS) ||
+                                 (old && old->flags & TCQ_F_INGRESS),
+       };
+
+       qdisc_offload_graft_helper(dev, NULL, new, old,
+                                  TC_SETUP_ROOT_QDISC, &graft_offload, extack);
+}
+
 static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
                         u32 portid, u32 seq, u16 flags, int event)
 {
@@ -957,7 +1022,6 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
 {
        struct Qdisc *q = old;
        struct net *net = dev_net(dev);
-       int err = 0;
 
        if (parent == NULL) {
                unsigned int i, num_q, ingress;
@@ -977,6 +1041,8 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
                if (dev->flags & IFF_UP)
                        dev_deactivate(dev);
 
+               qdisc_offload_graft_root(dev, new, old, extack);
+
                if (new && new->ops->attach)
                        goto skip;
 
@@ -1012,28 +1078,29 @@ skip:
                        dev_activate(dev);
        } else {
                const struct Qdisc_class_ops *cops = parent->ops->cl_ops;
+               unsigned long cl;
+               int err;
 
                /* Only support running class lockless if parent is lockless */
                if (new && (new->flags & TCQ_F_NOLOCK) &&
                    parent && !(parent->flags & TCQ_F_NOLOCK))
                        new->flags &= ~TCQ_F_NOLOCK;
 
-               err = -EOPNOTSUPP;
-               if (cops && cops->graft) {
-                       unsigned long cl = cops->find(parent, classid);
+               if (!cops || !cops->graft)
+                       return -EOPNOTSUPP;
 
-                       if (cl) {
-                               err = cops->graft(parent, cl, new, &old,
-                                                 extack);
-                       } else {
-                               NL_SET_ERR_MSG(extack, "Specified class not found");
-                               err = -ENOENT;
-                       }
+               cl = cops->find(parent, classid);
+               if (!cl) {
+                       NL_SET_ERR_MSG(extack, "Specified class not found");
+                       return -ENOENT;
                }
-               if (!err)
-                       notify_and_destroy(net, skb, n, classid, old, new);
+
+               err = cops->graft(parent, cl, new, &old, extack);
+               if (err)
+                       return err;
+               notify_and_destroy(net, skb, n, classid, old, new);
        }
-       return err;
+       return 0;
 }
 
 static int qdisc_block_indexes_set(struct Qdisc *sch, struct nlattr **tca,
index 1538d6f..1150f22 100644 (file)
@@ -30,7 +30,7 @@ struct etf_sched_data {
        int queue;
        s32 delta; /* in ns */
        ktime_t last; /* The txtime of the last skb sent to the netdevice. */
-       struct rb_root head;
+       struct rb_root_cached head;
        struct qdisc_watchdog watchdog;
        ktime_t (*get_time)(void);
 };
@@ -104,7 +104,7 @@ static struct sk_buff *etf_peek_timesortedlist(struct Qdisc *sch)
        struct etf_sched_data *q = qdisc_priv(sch);
        struct rb_node *p;
 
-       p = rb_first(&q->head);
+       p = rb_first_cached(&q->head);
        if (!p)
                return NULL;
 
@@ -117,8 +117,10 @@ static void reset_watchdog(struct Qdisc *sch)
        struct sk_buff *skb = etf_peek_timesortedlist(sch);
        ktime_t next;
 
-       if (!skb)
+       if (!skb) {
+               qdisc_watchdog_cancel(&q->watchdog);
                return;
+       }
 
        next = ktime_sub_ns(skb->tstamp, q->delta);
        qdisc_watchdog_schedule_ns(&q->watchdog, ktime_to_ns(next));
@@ -154,8 +156,9 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
                                      struct sk_buff **to_free)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node **p = &q->head.rb_node, *parent = NULL;
+       struct rb_node **p = &q->head.rb_root.rb_node, *parent = NULL;
        ktime_t txtime = nskb->tstamp;
+       bool leftmost = true;
 
        if (!is_packet_valid(sch, nskb)) {
                report_sock_error(nskb, EINVAL,
@@ -168,13 +171,15 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
 
                parent = *p;
                skb = rb_to_skb(parent);
-               if (ktime_after(txtime, skb->tstamp))
+               if (ktime_after(txtime, skb->tstamp)) {
                        p = &parent->rb_right;
-               else
+                       leftmost = false;
+               } else {
                        p = &parent->rb_left;
+               }
        }
        rb_link_node(&nskb->rbnode, parent, p);
-       rb_insert_color(&nskb->rbnode, &q->head);
+       rb_insert_color_cached(&nskb->rbnode, &q->head, leftmost);
 
        qdisc_qstats_backlog_inc(sch, nskb);
        sch->q.qlen++;
@@ -185,12 +190,42 @@ static int etf_enqueue_timesortedlist(struct sk_buff *nskb, struct Qdisc *sch,
        return NET_XMIT_SUCCESS;
 }
 
-static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
-                                bool drop)
+static void timesortedlist_drop(struct Qdisc *sch, struct sk_buff *skb,
+                               ktime_t now)
+{
+       struct etf_sched_data *q = qdisc_priv(sch);
+       struct sk_buff *to_free = NULL;
+       struct sk_buff *tmp = NULL;
+
+       skb_rbtree_walk_from_safe(skb, tmp) {
+               if (ktime_after(skb->tstamp, now))
+                       break;
+
+               rb_erase_cached(&skb->rbnode, &q->head);
+
+               /* The rbnode field in the skb re-uses these fields, now that
+                * we are done with the rbnode, reset them.
+                */
+               skb->next = NULL;
+               skb->prev = NULL;
+               skb->dev = qdisc_dev(sch);
+
+               report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
+
+               qdisc_qstats_backlog_dec(sch, skb);
+               qdisc_drop(skb, sch, &to_free);
+               qdisc_qstats_overlimit(sch);
+               sch->q.qlen--;
+       }
+
+       kfree_skb_list(to_free);
+}
+
+static void timesortedlist_remove(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
 
-       rb_erase(&skb->rbnode, &q->head);
+       rb_erase_cached(&skb->rbnode, &q->head);
 
        /* The rbnode field in the skb re-uses these fields, now that
         * we are done with the rbnode, reset them.
@@ -201,19 +236,9 @@ static void timesortedlist_erase(struct Qdisc *sch, struct sk_buff *skb,
 
        qdisc_qstats_backlog_dec(sch, skb);
 
-       if (drop) {
-               struct sk_buff *to_free = NULL;
+       qdisc_bstats_update(sch, skb);
 
-               report_sock_error(skb, ECANCELED, SO_EE_CODE_TXTIME_MISSED);
-
-               qdisc_drop(skb, sch, &to_free);
-               kfree_skb_list(to_free);
-               qdisc_qstats_overlimit(sch);
-       } else {
-               qdisc_bstats_update(sch, skb);
-
-               q->last = skb->tstamp;
-       }
+       q->last = skb->tstamp;
 
        sch->q.qlen--;
 }
@@ -232,7 +257,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
 
        /* Drop if packet has expired while in queue. */
        if (ktime_before(skb->tstamp, now)) {
-               timesortedlist_erase(sch, skb, true);
+               timesortedlist_drop(sch, skb, now);
                skb = NULL;
                goto out;
        }
@@ -241,7 +266,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
         * txtime from deadline to (now + delta).
         */
        if (q->deadline_mode) {
-               timesortedlist_erase(sch, skb, false);
+               timesortedlist_remove(sch, skb);
                skb->tstamp = now;
                goto out;
        }
@@ -250,7 +275,7 @@ static struct sk_buff *etf_dequeue_timesortedlist(struct Qdisc *sch)
 
        /* Dequeue only if now is within the [txtime - delta, txtime] range. */
        if (ktime_after(now, next))
-               timesortedlist_erase(sch, skb, false);
+               timesortedlist_remove(sch, skb);
        else
                skb = NULL;
 
@@ -386,14 +411,14 @@ static int etf_init(struct Qdisc *sch, struct nlattr *opt,
 static void timesortedlist_clear(struct Qdisc *sch)
 {
        struct etf_sched_data *q = qdisc_priv(sch);
-       struct rb_node *p = rb_first(&q->head);
+       struct rb_node *p = rb_first_cached(&q->head);
 
        while (p) {
                struct sk_buff *skb = rb_to_skb(p);
 
                p = rb_next(p);
 
-               rb_erase(&skb->rbnode, &q->head);
+               rb_erase_cached(&skb->rbnode, &q->head);
                rtnl_kfree_skbs(skb, skb);
                sch->q.qlen--;
        }
index 25a7cf6..1a662f2 100644 (file)
@@ -94,6 +94,7 @@ struct fq_sched_data {
        u32             flow_refill_delay;
        u32             flow_plimit;    /* max packets per flow */
        unsigned long   flow_max_rate;  /* optional max rate per flow */
+       u64             ce_threshold;
        u32             orphan_mask;    /* mask for orphaned skb */
        u32             low_rate_threshold;
        struct rb_root  *fq_root;
@@ -107,6 +108,7 @@ struct fq_sched_data {
        u64             stat_gc_flows;
        u64             stat_internal_packets;
        u64             stat_throttled;
+       u64             stat_ce_mark;
        u64             stat_flows_plimit;
        u64             stat_pkts_too_long;
        u64             stat_allocation_errors;
@@ -412,16 +414,21 @@ static void fq_check_throttled(struct fq_sched_data *q, u64 now)
 static struct sk_buff *fq_dequeue(struct Qdisc *sch)
 {
        struct fq_sched_data *q = qdisc_priv(sch);
-       u64 now = ktime_get_ns();
        struct fq_flow_head *head;
        struct sk_buff *skb;
        struct fq_flow *f;
        unsigned long rate;
        u32 plen;
+       u64 now;
+
+       if (!sch->q.qlen)
+               return NULL;
 
        skb = fq_dequeue_head(sch, &q->internal);
        if (skb)
                goto out;
+
+       now = ktime_get_ns();
        fq_check_throttled(q, now);
 begin:
        head = &q->new_flows;
@@ -454,6 +461,11 @@ begin:
                        fq_flow_set_throttled(q, f);
                        goto begin;
                }
+               if (time_next_packet &&
+                   (s64)(now - time_next_packet - q->ce_threshold) > 0) {
+                       INET_ECN_set_ce(skb);
+                       q->stat_ce_mark++;
+               }
        }
 
        skb = fq_dequeue_head(sch, f);
@@ -657,6 +669,7 @@ static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = {
        [TCA_FQ_BUCKETS_LOG]            = { .type = NLA_U32 },
        [TCA_FQ_FLOW_REFILL_DELAY]      = { .type = NLA_U32 },
        [TCA_FQ_LOW_RATE_THRESHOLD]     = { .type = NLA_U32 },
+       [TCA_FQ_CE_THRESHOLD]           = { .type = NLA_U32 },
 };
 
 static int fq_change(struct Qdisc *sch, struct nlattr *opt,
@@ -736,6 +749,10 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
        if (tb[TCA_FQ_ORPHAN_MASK])
                q->orphan_mask = nla_get_u32(tb[TCA_FQ_ORPHAN_MASK]);
 
+       if (tb[TCA_FQ_CE_THRESHOLD])
+               q->ce_threshold = (u64)NSEC_PER_USEC *
+                                 nla_get_u32(tb[TCA_FQ_CE_THRESHOLD]);
+
        if (!err) {
                sch_tree_unlock(sch);
                err = fq_resize(sch, fq_log);
@@ -786,6 +803,10 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt,
        q->fq_trees_log         = ilog2(1024);
        q->orphan_mask          = 1024 - 1;
        q->low_rate_threshold   = 550000 / 8;
+
+       /* Default ce_threshold of 4294 seconds */
+       q->ce_threshold         = (u64)NSEC_PER_USEC * ~0U;
+
        qdisc_watchdog_init_clockid(&q->watchdog, sch, CLOCK_MONOTONIC);
 
        if (opt)
@@ -799,6 +820,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt,
 static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct fq_sched_data *q = qdisc_priv(sch);
+       u64 ce_threshold = q->ce_threshold;
        struct nlattr *opts;
 
        opts = nla_nest_start(skb, TCA_OPTIONS);
@@ -807,6 +829,8 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
 
        /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore */
 
+       do_div(ce_threshold, NSEC_PER_USEC);
+
        if (nla_put_u32(skb, TCA_FQ_PLIMIT, sch->limit) ||
            nla_put_u32(skb, TCA_FQ_FLOW_PLIMIT, q->flow_plimit) ||
            nla_put_u32(skb, TCA_FQ_QUANTUM, q->quantum) ||
@@ -819,6 +843,7 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
            nla_put_u32(skb, TCA_FQ_ORPHAN_MASK, q->orphan_mask) ||
            nla_put_u32(skb, TCA_FQ_LOW_RATE_THRESHOLD,
                        q->low_rate_threshold) ||
+           nla_put_u32(skb, TCA_FQ_CE_THRESHOLD, (u32)ce_threshold) ||
            nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log))
                goto nla_put_failure;
 
@@ -848,6 +873,7 @@ static int fq_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
        st.throttled_flows        = q->throttled_flows;
        st.unthrottle_latency_ns  = min_t(unsigned long,
                                          q->unthrottle_latency_ns, ~0U);
+       st.ce_mark                = q->stat_ce_mark;
        sch_tree_unlock(sch);
 
        return gnet_stats_copy_app(d, &st, sizeof(st));
index 4a042ab..234afbf 100644 (file)
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
+#include <net/pkt_cls.h>
 #include <net/pkt_sched.h>
 #include <net/red.h>
 
 #define GRED_DEF_PRIO (MAX_DPs / 2)
 #define GRED_VQ_MASK (MAX_DPs - 1)
 
+#define GRED_VQ_RED_FLAGS      (TC_RED_ECN | TC_RED_HARDDROP)
+
 struct gred_sched_data;
 struct gred_sched;
 
 struct gred_sched_data {
        u32             limit;          /* HARD maximal queue length    */
        u32             DP;             /* the drop parameters */
-       u32             bytesin;        /* bytes seen on virtualQ so far*/
+       u32             red_flags;      /* virtualQ version of red_flags */
+       u64             bytesin;        /* bytes seen on virtualQ so far*/
        u32             packetsin;      /* packets seen on virtualQ so far*/
        u32             backlog;        /* bytes on the virtualQ */
        u8              prio;           /* the prio of this vq */
@@ -139,14 +143,27 @@ static inline void gred_store_wred_set(struct gred_sched *table,
        table->wred_set.qidlestart = q->vars.qidlestart;
 }
 
-static inline int gred_use_ecn(struct gred_sched *t)
+static int gred_use_ecn(struct gred_sched_data *q)
+{
+       return q->red_flags & TC_RED_ECN;
+}
+
+static int gred_use_harddrop(struct gred_sched_data *q)
 {
-       return t->red_flags & TC_RED_ECN;
+       return q->red_flags & TC_RED_HARDDROP;
 }
 
-static inline int gred_use_harddrop(struct gred_sched *t)
+static bool gred_per_vq_red_flags_used(struct gred_sched *table)
 {
-       return t->red_flags & TC_RED_HARDDROP;
+       unsigned int i;
+
+       /* Local per-vq flags couldn't have been set unless global are 0 */
+       if (table->red_flags)
+               return false;
+       for (i = 0; i < MAX_DPs; i++)
+               if (table->tab[i] && table->tab[i]->red_flags)
+                       return true;
+       return false;
 }
 
 static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
@@ -212,7 +229,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
        case RED_PROB_MARK:
                qdisc_qstats_overlimit(sch);
-               if (!gred_use_ecn(t) || !INET_ECN_set_ce(skb)) {
+               if (!gred_use_ecn(q) || !INET_ECN_set_ce(skb)) {
                        q->stats.prob_drop++;
                        goto congestion_drop;
                }
@@ -222,7 +239,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
        case RED_HARD_MARK:
                qdisc_qstats_overlimit(sch);
-               if (gred_use_harddrop(t) || !gred_use_ecn(t) ||
+               if (gred_use_harddrop(q) || !gred_use_ecn(q) ||
                    !INET_ECN_set_ce(skb)) {
                        q->stats.forced_drop++;
                        goto congestion_drop;
@@ -295,15 +312,103 @@ static void gred_reset(struct Qdisc *sch)
        }
 }
 
+static void gred_offload(struct Qdisc *sch, enum tc_gred_command command)
+{
+       struct gred_sched *table = qdisc_priv(sch);
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_gred_qopt_offload opt = {
+               .command        = command,
+               .handle         = sch->handle,
+               .parent         = sch->parent,
+       };
+
+       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+               return;
+
+       if (command == TC_GRED_REPLACE) {
+               unsigned int i;
+
+               opt.set.grio_on = gred_rio_mode(table);
+               opt.set.wred_on = gred_wred_mode(table);
+               opt.set.dp_cnt = table->DPs;
+               opt.set.dp_def = table->def;
+
+               for (i = 0; i < table->DPs; i++) {
+                       struct gred_sched_data *q = table->tab[i];
+
+                       if (!q)
+                               continue;
+                       opt.set.tab[i].present = true;
+                       opt.set.tab[i].limit = q->limit;
+                       opt.set.tab[i].prio = q->prio;
+                       opt.set.tab[i].min = q->parms.qth_min >> q->parms.Wlog;
+                       opt.set.tab[i].max = q->parms.qth_max >> q->parms.Wlog;
+                       opt.set.tab[i].is_ecn = gred_use_ecn(q);
+                       opt.set.tab[i].is_harddrop = gred_use_harddrop(q);
+                       opt.set.tab[i].probability = q->parms.max_P;
+                       opt.set.tab[i].backlog = &q->backlog;
+               }
+               opt.set.qstats = &sch->qstats;
+       }
+
+       dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_GRED, &opt);
+}
+
+static int gred_offload_dump_stats(struct Qdisc *sch)
+{
+       struct gred_sched *table = qdisc_priv(sch);
+       struct tc_gred_qopt_offload *hw_stats;
+       unsigned int i;
+       int ret;
+
+       hw_stats = kzalloc(sizeof(*hw_stats), GFP_KERNEL);
+       if (!hw_stats)
+               return -ENOMEM;
+
+       hw_stats->command = TC_GRED_STATS;
+       hw_stats->handle = sch->handle;
+       hw_stats->parent = sch->parent;
+
+       for (i = 0; i < MAX_DPs; i++)
+               if (table->tab[i])
+                       hw_stats->stats.xstats[i] = &table->tab[i]->stats;
+
+       ret = qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_GRED, hw_stats);
+       /* Even if driver returns failure adjust the stats - in case offload
+        * ended but driver still wants to adjust the values.
+        */
+       for (i = 0; i < MAX_DPs; i++) {
+               if (!table->tab[i])
+                       continue;
+               table->tab[i]->packetsin += hw_stats->stats.bstats[i].packets;
+               table->tab[i]->bytesin += hw_stats->stats.bstats[i].bytes;
+               table->tab[i]->backlog += hw_stats->stats.qstats[i].backlog;
+
+               _bstats_update(&sch->bstats,
+                              hw_stats->stats.bstats[i].bytes,
+                              hw_stats->stats.bstats[i].packets);
+               sch->qstats.qlen += hw_stats->stats.qstats[i].qlen;
+               sch->qstats.backlog += hw_stats->stats.qstats[i].backlog;
+               sch->qstats.drops += hw_stats->stats.qstats[i].drops;
+               sch->qstats.requeues += hw_stats->stats.qstats[i].requeues;
+               sch->qstats.overlimits += hw_stats->stats.qstats[i].overlimits;
+       }
+
+       kfree(hw_stats);
+       return ret;
+}
+
 static inline void gred_destroy_vq(struct gred_sched_data *q)
 {
        kfree(q);
 }
 
-static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
+static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps,
+                                struct netlink_ext_ack *extack)
 {
        struct gred_sched *table = qdisc_priv(sch);
        struct tc_gred_sopt *sopt;
+       bool red_flags_changed;
        int i;
 
        if (!dps)
@@ -311,13 +416,28 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
 
        sopt = nla_data(dps);
 
-       if (sopt->DPs > MAX_DPs || sopt->DPs == 0 ||
-           sopt->def_DP >= sopt->DPs)
+       if (sopt->DPs > MAX_DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "number of virtual queues too high");
+               return -EINVAL;
+       }
+       if (sopt->DPs == 0) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "number of virtual queues can't be 0");
+               return -EINVAL;
+       }
+       if (sopt->def_DP >= sopt->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "default virtual queue above virtual queue count");
                return -EINVAL;
+       }
+       if (sopt->flags && gred_per_vq_red_flags_used(table)) {
+               NL_SET_ERR_MSG_MOD(extack, "can't set per-Qdisc RED flags when per-virtual queue flags are used");
+               return -EINVAL;
+       }
 
        sch_tree_lock(sch);
        table->DPs = sopt->DPs;
        table->def = sopt->def_DP;
+       red_flags_changed = table->red_flags != sopt->flags;
        table->red_flags = sopt->flags;
 
        /*
@@ -337,6 +457,12 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
                gred_disable_wred_mode(table);
        }
 
+       if (red_flags_changed)
+               for (i = 0; i < table->DPs; i++)
+                       if (table->tab[i])
+                               table->tab[i]->red_flags =
+                                       table->red_flags & GRED_VQ_RED_FLAGS;
+
        for (i = table->DPs; i < MAX_DPs; i++) {
                if (table->tab[i]) {
                        pr_warn("GRED: Warning: Destroying shadowed VQ 0x%x\n",
@@ -346,25 +472,30 @@ static inline int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps)
                }
        }
 
+       gred_offload(sch, TC_GRED_REPLACE);
        return 0;
 }
 
 static inline int gred_change_vq(struct Qdisc *sch, int dp,
                                 struct tc_gred_qopt *ctl, int prio,
                                 u8 *stab, u32 max_P,
-                                struct gred_sched_data **prealloc)
+                                struct gred_sched_data **prealloc,
+                                struct netlink_ext_ack *extack)
 {
        struct gred_sched *table = qdisc_priv(sch);
        struct gred_sched_data *q = table->tab[dp];
 
-       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
+       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) {
+               NL_SET_ERR_MSG_MOD(extack, "invalid RED parameters");
                return -EINVAL;
+       }
 
        if (!q) {
                table->tab[dp] = q = *prealloc;
                *prealloc = NULL;
                if (!q)
                        return -ENOMEM;
+               q->red_flags = table->red_flags & GRED_VQ_RED_FLAGS;
        }
 
        q->DP = dp;
@@ -384,14 +515,127 @@ static inline int gred_change_vq(struct Qdisc *sch, int dp,
        return 0;
 }
 
+static const struct nla_policy gred_vq_policy[TCA_GRED_VQ_MAX + 1] = {
+       [TCA_GRED_VQ_DP]        = { .type = NLA_U32 },
+       [TCA_GRED_VQ_FLAGS]     = { .type = NLA_U32 },
+};
+
+static const struct nla_policy gred_vqe_policy[TCA_GRED_VQ_ENTRY_MAX + 1] = {
+       [TCA_GRED_VQ_ENTRY]     = { .type = NLA_NESTED },
+};
+
 static const struct nla_policy gred_policy[TCA_GRED_MAX + 1] = {
        [TCA_GRED_PARMS]        = { .len = sizeof(struct tc_gred_qopt) },
        [TCA_GRED_STAB]         = { .len = 256 },
        [TCA_GRED_DPS]          = { .len = sizeof(struct tc_gred_sopt) },
        [TCA_GRED_MAX_P]        = { .type = NLA_U32 },
        [TCA_GRED_LIMIT]        = { .type = NLA_U32 },
+       [TCA_GRED_VQ_LIST]      = { .type = NLA_NESTED },
 };
 
+static void gred_vq_apply(struct gred_sched *table, const struct nlattr *entry)
+{
+       struct nlattr *tb[TCA_GRED_VQ_MAX + 1];
+       u32 dp;
+
+       nla_parse_nested(tb, TCA_GRED_VQ_MAX, entry, gred_vq_policy, NULL);
+
+       dp = nla_get_u32(tb[TCA_GRED_VQ_DP]);
+
+       if (tb[TCA_GRED_VQ_FLAGS])
+               table->tab[dp]->red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]);
+}
+
+static void gred_vqs_apply(struct gred_sched *table, struct nlattr *vqs)
+{
+       const struct nlattr *attr;
+       int rem;
+
+       nla_for_each_nested(attr, vqs, rem) {
+               switch (nla_type(attr)) {
+               case TCA_GRED_VQ_ENTRY:
+                       gred_vq_apply(table, attr);
+                       break;
+               }
+       }
+}
+
+static int gred_vq_validate(struct gred_sched *table, u32 cdp,
+                           const struct nlattr *entry,
+                           struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_GRED_VQ_MAX + 1];
+       int err;
+       u32 dp;
+
+       err = nla_parse_nested(tb, TCA_GRED_VQ_MAX, entry, gred_vq_policy,
+                              extack);
+       if (err < 0)
+               return err;
+
+       if (!tb[TCA_GRED_VQ_DP]) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue with no index specified");
+               return -EINVAL;
+       }
+       dp = nla_get_u32(tb[TCA_GRED_VQ_DP]);
+       if (dp >= table->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue with index out of bounds");
+               return -EINVAL;
+       }
+       if (dp != cdp && !table->tab[dp]) {
+               NL_SET_ERR_MSG_MOD(extack, "Virtual queue not yet instantiated");
+               return -EINVAL;
+       }
+
+       if (tb[TCA_GRED_VQ_FLAGS]) {
+               u32 red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]);
+
+               if (table->red_flags && table->red_flags != red_flags) {
+                       NL_SET_ERR_MSG_MOD(extack, "can't change per-virtual queue RED flags when per-Qdisc flags are used");
+                       return -EINVAL;
+               }
+               if (red_flags & ~GRED_VQ_RED_FLAGS) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "invalid RED flags specified");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int gred_vqs_validate(struct gred_sched *table, u32 cdp,
+                            struct nlattr *vqs, struct netlink_ext_ack *extack)
+{
+       const struct nlattr *attr;
+       int rem, err;
+
+       err = nla_validate_nested(vqs, TCA_GRED_VQ_ENTRY_MAX,
+                                 gred_vqe_policy, extack);
+       if (err < 0)
+               return err;
+
+       nla_for_each_nested(attr, vqs, rem) {
+               switch (nla_type(attr)) {
+               case TCA_GRED_VQ_ENTRY:
+                       err = gred_vq_validate(table, cdp, attr, extack);
+                       if (err)
+                               return err;
+                       break;
+               default:
+                       NL_SET_ERR_MSG_MOD(extack, "GRED_VQ_LIST can contain only entry attributes");
+                       return -EINVAL;
+               }
+       }
+
+       if (rem > 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Trailing data after parsing virtual queue list");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int gred_change(struct Qdisc *sch, struct nlattr *opt,
                       struct netlink_ext_ack *extack)
 {
@@ -406,29 +650,39 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
        if (opt == NULL)
                return -EINVAL;
 
-       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL);
+       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, extack);
        if (err < 0)
                return err;
 
        if (tb[TCA_GRED_PARMS] == NULL && tb[TCA_GRED_STAB] == NULL) {
                if (tb[TCA_GRED_LIMIT] != NULL)
                        sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]);
-               return gred_change_table_def(sch, tb[TCA_GRED_DPS]);
+               return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack);
        }
 
        if (tb[TCA_GRED_PARMS] == NULL ||
            tb[TCA_GRED_STAB] == NULL ||
-           tb[TCA_GRED_LIMIT] != NULL)
+           tb[TCA_GRED_LIMIT] != NULL) {
+               NL_SET_ERR_MSG_MOD(extack, "can't configure Qdisc and virtual queue at the same time");
                return -EINVAL;
+       }
 
        max_P = tb[TCA_GRED_MAX_P] ? nla_get_u32(tb[TCA_GRED_MAX_P]) : 0;
 
-       err = -EINVAL;
        ctl = nla_data(tb[TCA_GRED_PARMS]);
        stab = nla_data(tb[TCA_GRED_STAB]);
 
-       if (ctl->DP >= table->DPs)
-               goto errout;
+       if (ctl->DP >= table->DPs) {
+               NL_SET_ERR_MSG_MOD(extack, "virtual queue index above virtual queue count");
+               return -EINVAL;
+       }
+
+       if (tb[TCA_GRED_VQ_LIST]) {
+               err = gred_vqs_validate(table, ctl->DP, tb[TCA_GRED_VQ_LIST],
+                                       extack);
+               if (err)
+                       return err;
+       }
 
        if (gred_rio_mode(table)) {
                if (ctl->prio == 0) {
@@ -448,9 +702,13 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
        prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
        sch_tree_lock(sch);
 
-       err = gred_change_vq(sch, ctl->DP, ctl, prio, stab, max_P, &prealloc);
+       err = gred_change_vq(sch, ctl->DP, ctl, prio, stab, max_P, &prealloc,
+                            extack);
        if (err < 0)
-               goto errout_locked;
+               goto err_unlock_free;
+
+       if (tb[TCA_GRED_VQ_LIST])
+               gred_vqs_apply(table, tb[TCA_GRED_VQ_LIST]);
 
        if (gred_rio_mode(table)) {
                gred_disable_wred_mode(table);
@@ -458,12 +716,15 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt,
                        gred_enable_wred_mode(table);
        }
 
-       err = 0;
+       sch_tree_unlock(sch);
+       kfree(prealloc);
+
+       gred_offload(sch, TC_GRED_REPLACE);
+       return 0;
 
-errout_locked:
+err_unlock_free:
        sch_tree_unlock(sch);
        kfree(prealloc);
-errout:
        return err;
 }
 
@@ -476,12 +737,15 @@ static int gred_init(struct Qdisc *sch, struct nlattr *opt,
        if (!opt)
                return -EINVAL;
 
-       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL);
+       err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, extack);
        if (err < 0)
                return err;
 
-       if (tb[TCA_GRED_PARMS] || tb[TCA_GRED_STAB])
+       if (tb[TCA_GRED_PARMS] || tb[TCA_GRED_STAB]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "virtual queue configuration can't be specified at initialization time");
                return -EINVAL;
+       }
 
        if (tb[TCA_GRED_LIMIT])
                sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]);
@@ -489,13 +753,13 @@ static int gred_init(struct Qdisc *sch, struct nlattr *opt,
                sch->limit = qdisc_dev(sch)->tx_queue_len
                             * psched_mtu(qdisc_dev(sch));
 
-       return gred_change_table_def(sch, tb[TCA_GRED_DPS]);
+       return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack);
 }
 
 static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct gred_sched *table = qdisc_priv(sch);
-       struct nlattr *parms, *opts = NULL;
+       struct nlattr *parms, *vqs, *opts = NULL;
        int i;
        u32 max_p[MAX_DPs];
        struct tc_gred_sopt sopt = {
@@ -505,6 +769,9 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
                .flags  = table->red_flags,
        };
 
+       if (gred_offload_dump_stats(sch))
+               goto nla_put_failure;
+
        opts = nla_nest_start(skb, TCA_OPTIONS);
        if (opts == NULL)
                goto nla_put_failure;
@@ -522,6 +789,7 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (nla_put_u32(skb, TCA_GRED_LIMIT, sch->limit))
                goto nla_put_failure;
 
+       /* Old style all-in-one dump of VQs */
        parms = nla_nest_start(skb, TCA_GRED_PARMS);
        if (parms == NULL)
                goto nla_put_failure;
@@ -572,6 +840,58 @@ append_opt:
 
        nla_nest_end(skb, parms);
 
+       /* Dump the VQs again, in more structured way */
+       vqs = nla_nest_start(skb, TCA_GRED_VQ_LIST);
+       if (!vqs)
+               goto nla_put_failure;
+
+       for (i = 0; i < MAX_DPs; i++) {
+               struct gred_sched_data *q = table->tab[i];
+               struct nlattr *vq;
+
+               if (!q)
+                       continue;
+
+               vq = nla_nest_start(skb, TCA_GRED_VQ_ENTRY);
+               if (!vq)
+                       goto nla_put_failure;
+
+               if (nla_put_u32(skb, TCA_GRED_VQ_DP, q->DP))
+                       goto nla_put_failure;
+
+               if (nla_put_u32(skb, TCA_GRED_VQ_FLAGS, q->red_flags))
+                       goto nla_put_failure;
+
+               /* Stats */
+               if (nla_put_u64_64bit(skb, TCA_GRED_VQ_STAT_BYTES, q->bytesin,
+                                     TCA_GRED_VQ_PAD))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PACKETS, q->packetsin))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_BACKLOG,
+                               gred_backlog(table, q, sch)))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_DROP,
+                               q->stats.prob_drop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_MARK,
+                               q->stats.prob_mark))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_DROP,
+                               q->stats.forced_drop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_MARK,
+                               q->stats.forced_mark))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PDROP, q->stats.pdrop))
+                       goto nla_put_failure;
+               if (nla_put_u32(skb, TCA_GRED_VQ_STAT_OTHER, q->stats.other))
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, vq);
+       }
+       nla_nest_end(skb, vqs);
+
        return nla_nest_end(skb, opts);
 
 nla_put_failure:
@@ -588,6 +908,7 @@ static void gred_destroy(struct Qdisc *sch)
                if (table->tab[i])
                        gred_destroy_vq(table->tab[i]);
        }
+       gred_offload(sch, TC_GRED_DESTROY);
 }
 
 static struct Qdisc_ops gred_qdisc_ops __read_mostly = {
index f20f3a0..203659b 100644 (file)
@@ -38,9 +38,8 @@ static int mq_offload(struct Qdisc *sch, enum tc_mq_command cmd)
        return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQ, &opt);
 }
 
-static void mq_offload_stats(struct Qdisc *sch)
+static int mq_offload_stats(struct Qdisc *sch)
 {
-       struct net_device *dev = qdisc_dev(sch);
        struct tc_mq_qopt_offload opt = {
                .command = TC_MQ_STATS,
                .handle = sch->handle,
@@ -50,8 +49,7 @@ static void mq_offload_stats(struct Qdisc *sch)
                },
        };
 
-       if (tc_can_offload(dev) && dev->netdev_ops->ndo_setup_tc)
-               dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQ, &opt);
+       return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_MQ, &opt);
 }
 
 static void mq_destroy(struct Qdisc *sch)
@@ -171,9 +169,8 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
 
                spin_unlock_bh(qdisc_lock(qdisc));
        }
-       mq_offload_stats(sch);
 
-       return 0;
+       return mq_offload_stats(sch);
 }
 
 static struct netdev_queue *mq_queue_get(struct Qdisc *sch, unsigned long cl)
@@ -196,6 +193,7 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
                    struct Qdisc **old, struct netlink_ext_ack *extack)
 {
        struct netdev_queue *dev_queue = mq_queue_get(sch, cl);
+       struct tc_mq_qopt_offload graft_offload;
        struct net_device *dev = qdisc_dev(sch);
 
        if (dev->flags & IFF_UP)
@@ -206,6 +204,14 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
                new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
        if (dev->flags & IFF_UP)
                dev_activate(dev);
+
+       graft_offload.handle = sch->handle;
+       graft_offload.graft_params.queue = cl - 1;
+       graft_offload.graft_params.child_handle = new ? new->handle : 0;
+       graft_offload.command = TC_MQ_GRAFT;
+
+       qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, *old,
+                                  TC_SETUP_QDISC_MQ, &graft_offload, extack);
        return 0;
 }
 
index 22cd46a..75046ec 100644 (file)
@@ -77,6 +77,10 @@ struct netem_sched_data {
        /* internal t(ime)fifo qdisc uses t_root and sch->limit */
        struct rb_root t_root;
 
+       /* a linear queue; reduces rbtree rebalancing when jitter is low */
+       struct sk_buff  *t_head;
+       struct sk_buff  *t_tail;
+
        /* optional qdisc for classful handling (NULL at netem init) */
        struct Qdisc    *qdisc;
 
@@ -369,26 +373,39 @@ static void tfifo_reset(struct Qdisc *sch)
                rb_erase(&skb->rbnode, &q->t_root);
                rtnl_kfree_skbs(skb, skb);
        }
+
+       rtnl_kfree_skbs(q->t_head, q->t_tail);
+       q->t_head = NULL;
+       q->t_tail = NULL;
 }
 
 static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        u64 tnext = netem_skb_cb(nskb)->time_to_send;
-       struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
 
-       while (*p) {
-               struct sk_buff *skb;
-
-               parent = *p;
-               skb = rb_to_skb(parent);
-               if (tnext >= netem_skb_cb(skb)->time_to_send)
-                       p = &parent->rb_right;
+       if (!q->t_tail || tnext >= netem_skb_cb(q->t_tail)->time_to_send) {
+               if (q->t_tail)
+                       q->t_tail->next = nskb;
                else
-                       p = &parent->rb_left;
+                       q->t_head = nskb;
+               q->t_tail = nskb;
+       } else {
+               struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
+
+               while (*p) {
+                       struct sk_buff *skb;
+
+                       parent = *p;
+                       skb = rb_to_skb(parent);
+                       if (tnext >= netem_skb_cb(skb)->time_to_send)
+                               p = &parent->rb_right;
+                       else
+                               p = &parent->rb_left;
+               }
+               rb_link_node(&nskb->rbnode, parent, p);
+               rb_insert_color(&nskb->rbnode, &q->t_root);
        }
-       rb_link_node(&nskb->rbnode, parent, p);
-       rb_insert_color(&nskb->rbnode, &q->t_root);
        sch->q.qlen++;
 }
 
@@ -533,9 +550,16 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                                t_skb = skb_rb_last(&q->t_root);
                                t_last = netem_skb_cb(t_skb);
                                if (!last ||
-                                   t_last->time_to_send > last->time_to_send) {
+                                   t_last->time_to_send > last->time_to_send)
+                                       last = t_last;
+                       }
+                       if (q->t_tail) {
+                               struct netem_skb_cb *t_last =
+                                       netem_skb_cb(q->t_tail);
+
+                               if (!last ||
+                                   t_last->time_to_send > last->time_to_send)
                                        last = t_last;
-                               }
                        }
 
                        if (last) {
@@ -614,11 +638,38 @@ static void get_slot_next(struct netem_sched_data *q, u64 now)
        q->slot.bytes_left = q->slot_config.max_bytes;
 }
 
+static struct sk_buff *netem_peek(struct netem_sched_data *q)
+{
+       struct sk_buff *skb = skb_rb_first(&q->t_root);
+       u64 t1, t2;
+
+       if (!skb)
+               return q->t_head;
+       if (!q->t_head)
+               return skb;
+
+       t1 = netem_skb_cb(skb)->time_to_send;
+       t2 = netem_skb_cb(q->t_head)->time_to_send;
+       if (t1 < t2)
+               return skb;
+       return q->t_head;
+}
+
+static void netem_erase_head(struct netem_sched_data *q, struct sk_buff *skb)
+{
+       if (skb == q->t_head) {
+               q->t_head = skb->next;
+               if (!q->t_head)
+                       q->t_tail = NULL;
+       } else {
+               rb_erase(&skb->rbnode, &q->t_root);
+       }
+}
+
 static struct sk_buff *netem_dequeue(struct Qdisc *sch)
 {
        struct netem_sched_data *q = qdisc_priv(sch);
        struct sk_buff *skb;
-       struct rb_node *p;
 
 tfifo_dequeue:
        skb = __qdisc_dequeue_head(&sch->q);
@@ -628,20 +679,18 @@ deliver:
                qdisc_bstats_update(sch, skb);
                return skb;
        }
-       p = rb_first(&q->t_root);
-       if (p) {
+       skb = netem_peek(q);
+       if (skb) {
                u64 time_to_send;
                u64 now = ktime_get_ns();
 
-               skb = rb_to_skb(p);
-
                /* if more time remaining? */
                time_to_send = netem_skb_cb(skb)->time_to_send;
                if (q->slot.slot_next && q->slot.slot_next < time_to_send)
                        get_slot_next(q, now);
 
-               if (time_to_send <= now &&  q->slot.slot_next <= now) {
-                       rb_erase(p, &q->t_root);
+               if (time_to_send <= now && q->slot.slot_next <= now) {
+                       netem_erase_head(q, skb);
                        sch->q.qlen--;
                        qdisc_qstats_backlog_dec(sch, skb);
                        skb->next = NULL;
index f8af986..cdf6870 100644 (file)
@@ -220,7 +220,6 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,
 
                qdisc_tree_reduce_backlog(child, child->q.qlen,
                                          child->qstats.backlog);
-               qdisc_put(child);
        }
 
        for (i = oldbands; i < q->bands; i++) {
@@ -230,6 +229,9 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,
        }
 
        sch_tree_unlock(sch);
+
+       for (i = q->bands; i < oldbands; i++)
+               qdisc_put(q->queues[i]);
        return 0;
 }
 
@@ -251,7 +253,6 @@ static int prio_init(struct Qdisc *sch, struct nlattr *opt,
 
 static int prio_dump_offload(struct Qdisc *sch)
 {
-       struct net_device *dev = qdisc_dev(sch);
        struct tc_prio_qopt_offload hw_stats = {
                .command = TC_PRIO_STATS,
                .handle = sch->handle,
@@ -263,21 +264,8 @@ static int prio_dump_offload(struct Qdisc *sch)
                        },
                },
        };
-       int err;
-
-       sch->flags &= ~TCQ_F_OFFLOADED;
-       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
-               return 0;
-
-       err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_PRIO,
-                                           &hw_stats);
-       if (err == -EOPNOTSUPP)
-               return 0;
 
-       if (!err)
-               sch->flags |= TCQ_F_OFFLOADED;
-
-       return err;
+       return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_PRIO, &hw_stats);
 }
 
 static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
@@ -309,43 +297,22 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
 {
        struct prio_sched_data *q = qdisc_priv(sch);
        struct tc_prio_qopt_offload graft_offload;
-       struct net_device *dev = qdisc_dev(sch);
        unsigned long band = arg - 1;
-       bool any_qdisc_is_offloaded;
-       int err;
 
        if (new == NULL)
                new = &noop_qdisc;
 
        *old = qdisc_replace(sch, new, &q->queues[band]);
 
-       if (!tc_can_offload(dev))
-               return 0;
-
        graft_offload.handle = sch->handle;
        graft_offload.parent = sch->parent;
        graft_offload.graft_params.band = band;
        graft_offload.graft_params.child_handle = new->handle;
        graft_offload.command = TC_PRIO_GRAFT;
 
-       err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_PRIO,
-                                           &graft_offload);
-
-       /* Don't report error if the graft is part of destroy operation. */
-       if (err && new != &noop_qdisc) {
-               /* Don't report error if the parent, the old child and the new
-                * one are not offloaded.
-                */
-               any_qdisc_is_offloaded = sch->flags & TCQ_F_OFFLOADED;
-               any_qdisc_is_offloaded |= new->flags & TCQ_F_OFFLOADED;
-               if (*old)
-                       any_qdisc_is_offloaded |= (*old)->flags &
-                                                  TCQ_F_OFFLOADED;
-
-               if (any_qdisc_is_offloaded)
-                       NL_SET_ERR_MSG(extack, "Offloading graft operation failed.");
-       }
-
+       qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, *old,
+                                  TC_SETUP_QDISC_PRIO, &graft_offload,
+                                  extack);
        return 0;
 }
 
index 3ce6c0a..9df9942 100644 (file)
@@ -166,7 +166,9 @@ static int red_offload(struct Qdisc *sch, bool enable)
                opt.set.min = q->parms.qth_min >> q->parms.Wlog;
                opt.set.max = q->parms.qth_max >> q->parms.Wlog;
                opt.set.probability = q->parms.max_P;
+               opt.set.limit = q->limit;
                opt.set.is_ecn = red_use_ecn(q);
+               opt.set.is_harddrop = red_use_harddrop(q);
                opt.set.qstats = &sch->qstats;
        } else {
                opt.command = TC_RED_DESTROY;
@@ -193,10 +195,10 @@ static const struct nla_policy red_policy[TCA_RED_MAX + 1] = {
 static int red_change(struct Qdisc *sch, struct nlattr *opt,
                      struct netlink_ext_ack *extack)
 {
+       struct Qdisc *old_child = NULL, *child = NULL;
        struct red_sched_data *q = qdisc_priv(sch);
        struct nlattr *tb[TCA_RED_MAX + 1];
        struct tc_red_qopt *ctl;
-       struct Qdisc *child = NULL;
        int err;
        u32 max_P;
 
@@ -233,7 +235,7 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt,
        if (child) {
                qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
                                          q->qdisc->qstats.backlog);
-               qdisc_put(q->qdisc);
+               old_child = q->qdisc;
                q->qdisc = child;
        }
 
@@ -252,7 +254,11 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt,
                red_start_of_idle_period(&q->vars);
 
        sch_tree_unlock(sch);
+
        red_offload(sch, true);
+
+       if (old_child)
+               qdisc_put(old_child);
        return 0;
 }
 
@@ -279,9 +285,8 @@ static int red_init(struct Qdisc *sch, struct nlattr *opt,
        return red_change(sch, opt, extack);
 }
 
-static int red_dump_offload_stats(struct Qdisc *sch, struct tc_red_qopt *opt)
+static int red_dump_offload_stats(struct Qdisc *sch)
 {
-       struct net_device *dev = qdisc_dev(sch);
        struct tc_red_qopt_offload hw_stats = {
                .command = TC_RED_STATS,
                .handle = sch->handle,
@@ -291,22 +296,8 @@ static int red_dump_offload_stats(struct Qdisc *sch, struct tc_red_qopt *opt)
                        .stats.qstats = &sch->qstats,
                },
        };
-       int err;
-
-       sch->flags &= ~TCQ_F_OFFLOADED;
-
-       if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
-               return 0;
-
-       err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED,
-                                           &hw_stats);
-       if (err == -EOPNOTSUPP)
-               return 0;
 
-       if (!err)
-               sch->flags |= TCQ_F_OFFLOADED;
-
-       return err;
+       return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_RED, &hw_stats);
 }
 
 static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
@@ -324,7 +315,7 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
        };
        int err;
 
-       err = red_dump_offload_stats(sch, &opt);
+       err = red_dump_offload_stats(sch);
        if (err)
                goto nla_put_failure;
 
@@ -377,6 +368,21 @@ static int red_dump_class(struct Qdisc *sch, unsigned long cl,
        return 0;
 }
 
+static void red_graft_offload(struct Qdisc *sch,
+                             struct Qdisc *new, struct Qdisc *old,
+                             struct netlink_ext_ack *extack)
+{
+       struct tc_red_qopt_offload graft_offload = {
+               .handle         = sch->handle,
+               .parent         = sch->parent,
+               .child_handle   = new->handle,
+               .command        = TC_RED_GRAFT,
+       };
+
+       qdisc_offload_graft_helper(qdisc_dev(sch), sch, new, old,
+                                  TC_SETUP_QDISC_RED, &graft_offload, extack);
+}
+
 static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                     struct Qdisc **old, struct netlink_ext_ack *extack)
 {
@@ -386,6 +392,8 @@ static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                new = &noop_qdisc;
 
        *old = qdisc_replace(sch, new, &q->qdisc);
+
+       red_graft_offload(sch, new, *old, extack);
        return 0;
 }
 
index 914750b..201c888 100644 (file)
@@ -132,6 +132,8 @@ static struct sctp_association *sctp_association_init(
         */
        asoc->max_burst = sp->max_burst;
 
+       asoc->subscribe = sp->subscribe;
+
        /* initialize association timers */
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] = asoc->rto_initial;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] = asoc->rto_initial;
index 7df3704..ebf28ad 100644 (file)
@@ -337,6 +337,34 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
        return match;
 }
 
+int sctp_bind_addrs_check(struct sctp_sock *sp,
+                         struct sctp_sock *sp2, int cnt2)
+{
+       struct sctp_bind_addr *bp2 = &sp2->ep->base.bind_addr;
+       struct sctp_bind_addr *bp = &sp->ep->base.bind_addr;
+       struct sctp_sockaddr_entry *laddr, *laddr2;
+       bool exist = false;
+       int cnt = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(laddr, &bp->address_list, list) {
+               list_for_each_entry_rcu(laddr2, &bp2->address_list, list) {
+                       if (sp->pf->af->cmp_addr(&laddr->a, &laddr2->a) &&
+                           laddr->valid && laddr2->valid) {
+                               exist = true;
+                               goto next;
+                       }
+               }
+               cnt = 0;
+               break;
+next:
+               cnt++;
+       }
+       rcu_read_unlock();
+
+       return (cnt == cnt2) ? 0 : (exist ? -EEXIST : 1);
+}
+
 /* Does the address 'addr' conflict with any addresses in
  * the bp.
  */
index d2048de..64bef31 100644 (file)
@@ -86,11 +86,10 @@ void sctp_datamsg_free(struct sctp_datamsg *msg)
 /* Final destructruction of datamsg memory. */
 static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
 {
+       struct sctp_association *asoc = NULL;
        struct list_head *pos, *temp;
        struct sctp_chunk *chunk;
-       struct sctp_sock *sp;
        struct sctp_ulpevent *ev;
-       struct sctp_association *asoc = NULL;
        int error = 0, notify;
 
        /* If we failed, we may need to notify. */
@@ -108,9 +107,8 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
                        else
                                error = asoc->outqueue.error;
 
-                       sp = sctp_sk(asoc->base.sk);
-                       notify = sctp_ulpevent_type_enabled(SCTP_SEND_FAILED,
-                                                           &sp->subscribe);
+                       notify = sctp_ulpevent_type_enabled(asoc->subscribe,
+                                                           SCTP_SEND_FAILED);
                }
 
                /* Generate a SEND FAILED event only if enabled. */
index 5c36a99..d7a649d 100644 (file)
@@ -57,6 +57,7 @@
 #include <net/sctp/checksum.h>
 #include <net/net_namespace.h>
 #include <linux/rhashtable.h>
+#include <net/sock_reuseport.h>
 
 /* Forward declarations for internal helpers. */
 static int sctp_rcv_ootb(struct sk_buff *);
@@ -65,8 +66,10 @@ static struct sctp_association *__sctp_rcv_lookup(struct net *net,
                                      const union sctp_addr *paddr,
                                      const union sctp_addr *laddr,
                                      struct sctp_transport **transportp);
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
-                                               const union sctp_addr *laddr);
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(
+                                       struct net *net, struct sk_buff *skb,
+                                       const union sctp_addr *laddr,
+                                       const union sctp_addr *daddr);
 static struct sctp_association *__sctp_lookup_association(
                                        struct net *net,
                                        const union sctp_addr *local,
@@ -171,7 +174,7 @@ int sctp_rcv(struct sk_buff *skb)
        asoc = __sctp_rcv_lookup(net, skb, &src, &dest, &transport);
 
        if (!asoc)
-               ep = __sctp_rcv_lookup_endpoint(net, &dest);
+               ep = __sctp_rcv_lookup_endpoint(net, skb, &dest, &src);
 
        /* Retrieve the common input handling substructure. */
        rcvr = asoc ? &asoc->base : &ep->base;
@@ -574,7 +577,7 @@ void sctp_err_finish(struct sock *sk, struct sctp_transport *t)
  * is probably better.
  *
  */
-void sctp_v4_err(struct sk_buff *skb, __u32 info)
+int sctp_v4_err(struct sk_buff *skb, __u32 info)
 {
        const struct iphdr *iph = (const struct iphdr *)skb->data;
        const int ihlen = iph->ihl * 4;
@@ -599,7 +602,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
        skb->transport_header = savesctp;
        if (!sk) {
                __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
-               return;
+               return -ENOENT;
        }
        /* Warning:  The sock lock is held.  Remember to call
         * sctp_err_finish!
@@ -653,6 +656,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
 
 out_unlock:
        sctp_err_finish(sk, transport);
+       return 0;
 }
 
 /*
@@ -720,43 +724,87 @@ discard:
 }
 
 /* Insert endpoint into the hash table.  */
-static void __sctp_hash_endpoint(struct sctp_endpoint *ep)
+static int __sctp_hash_endpoint(struct sctp_endpoint *ep)
 {
-       struct net *net = sock_net(ep->base.sk);
-       struct sctp_ep_common *epb;
+       struct sock *sk = ep->base.sk;
+       struct net *net = sock_net(sk);
        struct sctp_hashbucket *head;
+       struct sctp_ep_common *epb;
 
        epb = &ep->base;
-
        epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
        head = &sctp_ep_hashtable[epb->hashent];
 
+       if (sk->sk_reuseport) {
+               bool any = sctp_is_ep_boundall(sk);
+               struct sctp_ep_common *epb2;
+               struct list_head *list;
+               int cnt = 0, err = 1;
+
+               list_for_each(list, &ep->base.bind_addr.address_list)
+                       cnt++;
+
+               sctp_for_each_hentry(epb2, &head->chain) {
+                       struct sock *sk2 = epb2->sk;
+
+                       if (!net_eq(sock_net(sk2), net) || sk2 == sk ||
+                           !uid_eq(sock_i_uid(sk2), sock_i_uid(sk)) ||
+                           !sk2->sk_reuseport)
+                               continue;
+
+                       err = sctp_bind_addrs_check(sctp_sk(sk2),
+                                                   sctp_sk(sk), cnt);
+                       if (!err) {
+                               err = reuseport_add_sock(sk, sk2, any);
+                               if (err)
+                                       return err;
+                               break;
+                       } else if (err < 0) {
+                               return err;
+                       }
+               }
+
+               if (err) {
+                       err = reuseport_alloc(sk, any);
+                       if (err)
+                               return err;
+               }
+       }
+
        write_lock(&head->lock);
        hlist_add_head(&epb->node, &head->chain);
        write_unlock(&head->lock);
+       return 0;
 }
 
 /* Add an endpoint to the hash. Local BH-safe. */
-void sctp_hash_endpoint(struct sctp_endpoint *ep)
+int sctp_hash_endpoint(struct sctp_endpoint *ep)
 {
+       int err;
+
        local_bh_disable();
-       __sctp_hash_endpoint(ep);
+       err = __sctp_hash_endpoint(ep);
        local_bh_enable();
+
+       return err;
 }
 
 /* Remove endpoint from the hash table.  */
 static void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
 {
-       struct net *net = sock_net(ep->base.sk);
+       struct sock *sk = ep->base.sk;
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
 
        epb = &ep->base;
 
-       epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port);
+       epb->hashent = sctp_ep_hashfn(sock_net(sk), epb->bind_addr.port);
 
        head = &sctp_ep_hashtable[epb->hashent];
 
+       if (rcu_access_pointer(sk->sk_reuseport_cb))
+               reuseport_detach_sock(sk);
+
        write_lock(&head->lock);
        hlist_del_init(&epb->node);
        write_unlock(&head->lock);
@@ -770,16 +818,35 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep)
        local_bh_enable();
 }
 
+static inline __u32 sctp_hashfn(const struct net *net, __be16 lport,
+                               const union sctp_addr *paddr, __u32 seed)
+{
+       __u32 addr;
+
+       if (paddr->sa.sa_family == AF_INET6)
+               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+       else
+               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
+
+       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
+                            (__force __u32)lport, net_hash_mix(net), seed);
+}
+
 /* Look up an endpoint. */
-static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
-                                               const union sctp_addr *laddr)
+static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(
+                                       struct net *net, struct sk_buff *skb,
+                                       const union sctp_addr *laddr,
+                                       const union sctp_addr *paddr)
 {
        struct sctp_hashbucket *head;
        struct sctp_ep_common *epb;
        struct sctp_endpoint *ep;
+       struct sock *sk;
+       __be16 lport;
        int hash;
 
-       hash = sctp_ep_hashfn(net, ntohs(laddr->v4.sin_port));
+       lport = laddr->v4.sin_port;
+       hash = sctp_ep_hashfn(net, ntohs(lport));
        head = &sctp_ep_hashtable[hash];
        read_lock(&head->lock);
        sctp_for_each_hentry(epb, &head->chain) {
@@ -791,6 +858,15 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(struct net *net,
        ep = sctp_sk(net->sctp.ctl_sock)->ep;
 
 hit:
+       sk = ep->base.sk;
+       if (sk->sk_reuseport) {
+               __u32 phash = sctp_hashfn(net, lport, paddr, 0);
+
+               sk = reuseport_select_sock(sk, phash, skb,
+                                          sizeof(struct sctphdr));
+               if (sk)
+                       ep = sctp_sk(sk)->ep;
+       }
        sctp_endpoint_hold(ep);
        read_unlock(&head->lock);
        return ep;
@@ -829,35 +905,17 @@ out:
 static inline __u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
 {
        const struct sctp_transport *t = data;
-       const union sctp_addr *paddr = &t->ipaddr;
-       const struct net *net = sock_net(t->asoc->base.sk);
-       __be16 lport = htons(t->asoc->base.bind_addr.port);
-       __u32 addr;
-
-       if (paddr->sa.sa_family == AF_INET6)
-               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
-       else
-               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
 
-       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
-                            (__force __u32)lport, net_hash_mix(net), seed);
+       return sctp_hashfn(sock_net(t->asoc->base.sk),
+                          htons(t->asoc->base.bind_addr.port),
+                          &t->ipaddr, seed);
 }
 
 static inline __u32 sctp_hash_key(const void *data, u32 len, u32 seed)
 {
        const struct sctp_hash_cmp_arg *x = data;
-       const union sctp_addr *paddr = x->paddr;
-       const struct net *net = x->net;
-       __be16 lport = x->lport;
-       __u32 addr;
 
-       if (paddr->sa.sa_family == AF_INET6)
-               addr = jhash(&paddr->v6.sin6_addr, 16, seed);
-       else
-               addr = (__force __u32)paddr->v4.sin_addr.s_addr;
-
-       return  jhash_3words(addr, ((__force __u32)paddr->v4.sin_port) << 16 |
-                            (__force __u32)lport, net_hash_mix(net), seed);
+       return sctp_hashfn(x->net, x->lport, x->paddr, seed);
 }
 
 static const struct rhashtable_params sctp_hash_params = {
index fc6c5e4..6e27c62 100644 (file)
@@ -138,7 +138,7 @@ static struct notifier_block sctp_inet6addr_notifier = {
 };
 
 /* ICMP error handler. */
-static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+static int sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        u8 type, u8 code, int offset, __be32 info)
 {
        struct inet6_dev *idev;
@@ -147,7 +147,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct sctp_transport *transport;
        struct ipv6_pinfo *np;
        __u16 saveip, savesctp;
-       int err;
+       int err, ret = 0;
        struct net *net = dev_net(skb->dev);
 
        idev = in6_dev_get(skb->dev);
@@ -163,6 +163,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        skb->transport_header = savesctp;
        if (!sk) {
                __ICMP6_INC_STATS(net, idev, ICMP6_MIB_INERRORS);
+               ret = -ENOENT;
                goto out;
        }
 
@@ -202,6 +203,8 @@ out_unlock:
 out:
        if (likely(idev != NULL))
                in6_dev_put(idev);
+
+       return ret;
 }
 
 static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
index c0817f7..a8c4c33 100644 (file)
@@ -53,7 +53,7 @@
 int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \
                            void *arg) { \
        int error = 0; \
-       enum sctp_event event_type; union sctp_subtype subtype; \
+       enum sctp_event_type event_type; union sctp_subtype subtype; \
        enum sctp_state state; \
        struct sctp_endpoint *ep; \
        \
index 85d3930..1d143bc 100644 (file)
@@ -52,7 +52,7 @@
 #include <net/sctp/sm.h>
 #include <net/sctp/stream_sched.h>
 
-static int sctp_cmd_interpreter(enum sctp_event event_type,
+static int sctp_cmd_interpreter(enum sctp_event_type event_type,
                                union sctp_subtype subtype,
                                enum sctp_state state,
                                struct sctp_endpoint *ep,
@@ -61,7 +61,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
                                enum sctp_disposition status,
                                struct sctp_cmd_seq *commands,
                                gfp_t gfp);
-static int sctp_side_effects(enum sctp_event event_type,
+static int sctp_side_effects(enum sctp_event_type event_type,
                             union sctp_subtype subtype,
                             enum sctp_state state,
                             struct sctp_endpoint *ep,
@@ -623,7 +623,7 @@ static void sctp_cmd_init_failed(struct sctp_cmd_seq *commands,
 /* Worker routine to handle SCTP_CMD_ASSOC_FAILED.  */
 static void sctp_cmd_assoc_failed(struct sctp_cmd_seq *commands,
                                  struct sctp_association *asoc,
-                                 enum sctp_event event_type,
+                                 enum sctp_event_type event_type,
                                  union sctp_subtype subtype,
                                  struct sctp_chunk *chunk,
                                  unsigned int error)
@@ -1162,7 +1162,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc)
  * If you want to understand all of lksctp, this is a
  * good place to start.
  */
-int sctp_do_sm(struct net *net, enum sctp_event event_type,
+int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
               union sctp_subtype subtype, enum sctp_state state,
               struct sctp_endpoint *ep, struct sctp_association *asoc,
               void *event_arg, gfp_t gfp)
@@ -1199,7 +1199,7 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type,
 /*****************************************************************
  * This the master state function side effect processing function.
  *****************************************************************/
-static int sctp_side_effects(enum sctp_event event_type,
+static int sctp_side_effects(enum sctp_event_type event_type,
                             union sctp_subtype subtype,
                             enum sctp_state state,
                             struct sctp_endpoint *ep,
@@ -1285,7 +1285,7 @@ bail:
  ********************************************************************/
 
 /* This is the side-effect interpreter.  */
-static int sctp_cmd_interpreter(enum sctp_event event_type,
+static int sctp_cmd_interpreter(enum sctp_event_type event_type,
                                union sctp_subtype subtype,
                                enum sctp_state state,
                                struct sctp_endpoint *ep,
index 691d9dc..d239b94 100644 (file)
@@ -79,7 +79,7 @@ static const struct sctp_sm_table_entry bug = {
 
 const struct sctp_sm_table_entry *sctp_sm_lookup_event(
                                        struct net *net,
-                                       enum sctp_event event_type,
+                                       enum sctp_event_type event_type,
                                        enum sctp_state state,
                                        union sctp_subtype event_subtype)
 {
index b8cebd5..f93c3cf 100644 (file)
@@ -2230,7 +2230,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        if (sp->recvrcvinfo)
                sctp_ulpevent_read_rcvinfo(event, msg);
        /* Check if we allow SCTP_SNDRCVINFO. */
-       if (sp->subscribe.sctp_data_io_event)
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
                sctp_ulpevent_read_sndrcvinfo(event, msg);
 
        err = copied;
@@ -2304,22 +2304,33 @@ static int sctp_setsockopt_disable_fragments(struct sock *sk,
 static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
                                  unsigned int optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_ulpevent *event;
+       int i;
 
        if (optlen > sizeof(struct sctp_event_subscribe))
                return -EINVAL;
-       if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
+
+       if (copy_from_user(&subscribe, optval, optlen))
                return -EFAULT;
 
+       for (i = 0; i < optlen; i++)
+               sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
+                                      sn_type[i]);
+
+       list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+               asoc->subscribe = sctp_sk(sk)->subscribe;
+
        /* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
         * if there is no data to be sent or retransmit, the stack will
         * immediately send up this notification.
         */
-       if (sctp_ulpevent_type_enabled(SCTP_SENDER_DRY_EVENT,
-                                      &sctp_sk(sk)->subscribe)) {
-               asoc = sctp_id2assoc(sk, 0);
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
+               struct sctp_ulpevent *event;
 
+               asoc = sctp_id2assoc(sk, 0);
                if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
                        event = sctp_ulpevent_make_sender_dry_event(asoc,
                                        GFP_USER | __GFP_NOWARN);
@@ -4260,6 +4271,57 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
        return 0;
 }
 
+static int sctp_setsockopt_event(struct sock *sk, char __user *optval,
+                                unsigned int optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_ulpevent *event;
+       struct sctp_event param;
+       int retval = 0;
+
+       if (optlen < sizeof(param)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       optlen = sizeof(param);
+       if (copy_from_user(&param, optval, optlen)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       if (!asoc) {
+               sctp_ulpevent_type_set(&sctp_sk(sk)->subscribe,
+                                      param.se_type, param.se_on);
+               goto out;
+       }
+
+       sctp_ulpevent_type_set(&asoc->subscribe, param.se_type, param.se_on);
+
+       if (param.se_type == SCTP_SENDER_DRY_EVENT && param.se_on) {
+               if (sctp_outq_is_empty(&asoc->outqueue)) {
+                       event = sctp_ulpevent_make_sender_dry_event(asoc,
+                                       GFP_USER | __GFP_NOWARN);
+                       if (!event) {
+                               retval = -ENOMEM;
+                               goto out;
+                       }
+
+                       asoc->stream.si->enqueue_event(&asoc->ulpq, event);
+               }
+       }
+
+out:
+       return retval;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -4457,6 +4519,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_setsockopt_reuse_port(sk, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_setsockopt_event(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -4705,7 +4770,7 @@ static int sctp_init_sock(struct sock *sk)
        /* Initialize default event subscriptions. By default, all the
         * options are off.
         */
-       memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
+       sp->subscribe = 0;
 
        /* Default Peer Address Parameters.  These defaults can
         * be modified via SCTP_PEER_ADDR_PARAMS
@@ -5250,14 +5315,24 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       int i;
+
        if (len == 0)
                return -EINVAL;
        if (len > sizeof(struct sctp_event_subscribe))
                len = sizeof(struct sctp_event_subscribe);
        if (put_user(len, optlen))
                return -EFAULT;
-       if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
+
+       for (i = 0; i < len; i++)
+               sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
+                                                       SCTP_SN_TYPE_BASE + i);
+
+       if (copy_to_user(optval, &subscribe, len))
                return -EFAULT;
+
        return 0;
 }
 
@@ -7392,6 +7467,37 @@ static int sctp_getsockopt_reuse_port(struct sock *sk, int len,
        return 0;
 }
 
+static int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
+                                int __user *optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_event param;
+       __u16 subscribe;
+
+       if (len < sizeof(param))
+               return -EINVAL;
+
+       len = sizeof(param);
+       if (copy_from_user(&param, optval, len))
+               return -EFAULT;
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX)
+               return -EINVAL;
+
+       asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
+       param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       if (copy_to_user(optval, &param, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int sctp_getsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int __user *optlen)
 {
@@ -7590,6 +7696,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_getsockopt_event(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -7627,8 +7736,10 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 
 static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 {
-       bool reuse = (sk->sk_reuse || sctp_sk(sk)->reuse);
+       struct sctp_sock *sp = sctp_sk(sk);
+       bool reuse = (sk->sk_reuse || sp->reuse);
        struct sctp_bind_hashbucket *head; /* hash list */
+       kuid_t uid = sock_i_uid(sk);
        struct sctp_bind_bucket *pp;
        unsigned short snum;
        int ret;
@@ -7704,7 +7815,10 @@ pp_found:
 
                pr_debug("%s: found a possible match\n", __func__);
 
-               if (pp->fastreuse && reuse && sk->sk_state != SCTP_SS_LISTENING)
+               if ((pp->fastreuse && reuse &&
+                    sk->sk_state != SCTP_SS_LISTENING) ||
+                   (pp->fastreuseport && sk->sk_reuseport &&
+                    uid_eq(pp->fastuid, uid)))
                        goto success;
 
                /* Run through the list of sockets bound to the port
@@ -7718,16 +7832,18 @@ pp_found:
                 * in an endpoint.
                 */
                sk_for_each_bound(sk2, &pp->owner) {
-                       struct sctp_endpoint *ep2;
-                       ep2 = sctp_sk(sk2)->ep;
+                       struct sctp_sock *sp2 = sctp_sk(sk2);
+                       struct sctp_endpoint *ep2 = sp2->ep;
 
                        if (sk == sk2 ||
-                           (reuse && (sk2->sk_reuse || sctp_sk(sk2)->reuse) &&
-                            sk2->sk_state != SCTP_SS_LISTENING))
+                           (reuse && (sk2->sk_reuse || sp2->reuse) &&
+                            sk2->sk_state != SCTP_SS_LISTENING) ||
+                           (sk->sk_reuseport && sk2->sk_reuseport &&
+                            uid_eq(uid, sock_i_uid(sk2))))
                                continue;
 
-                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
-                                                sctp_sk(sk2), sctp_sk(sk))) {
+                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr,
+                                                   addr, sp2, sp)) {
                                ret = (long)sk2;
                                goto fail_unlock;
                        }
@@ -7750,19 +7866,32 @@ pp_not_found:
                        pp->fastreuse = 1;
                else
                        pp->fastreuse = 0;
-       } else if (pp->fastreuse &&
-                  (!reuse || sk->sk_state == SCTP_SS_LISTENING))
-               pp->fastreuse = 0;
+
+               if (sk->sk_reuseport) {
+                       pp->fastreuseport = 1;
+                       pp->fastuid = uid;
+               } else {
+                       pp->fastreuseport = 0;
+               }
+       } else {
+               if (pp->fastreuse &&
+                   (!reuse || sk->sk_state == SCTP_SS_LISTENING))
+                       pp->fastreuse = 0;
+
+               if (pp->fastreuseport &&
+                   (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
+                       pp->fastreuseport = 0;
+       }
 
        /* We are set, so fill up all the data in the hash table
         * entry, tie the socket list information with the rest of the
         * sockets FIXME: Blurry, NPI (ipg).
         */
 success:
-       if (!sctp_sk(sk)->bind_hash) {
+       if (!sp->bind_hash) {
                inet_sk(sk)->inet_num = snum;
                sk_add_bind_node(sk, &pp->owner);
-               sctp_sk(sk)->bind_hash = pp;
+               sp->bind_hash = pp;
        }
        ret = 0;
 
@@ -7835,8 +7964,7 @@ static int sctp_listen_start(struct sock *sk, int backlog)
        }
 
        sk->sk_max_ack_backlog = backlog;
-       sctp_hash_endpoint(ep);
-       return 0;
+       return sctp_hash_endpoint(ep);
 }
 
 /*
index 0a78cdf..a6bf215 100644 (file)
@@ -140,7 +140,7 @@ static void sctp_intl_store_reasm(struct sctp_ulpq *ulpq,
                                  struct sctp_ulpevent *event)
 {
        struct sctp_ulpevent *cevent;
-       struct sk_buff *pos;
+       struct sk_buff *pos, *loc;
 
        pos = skb_peek_tail(&ulpq->reasm);
        if (!pos) {
@@ -166,23 +166,30 @@ static void sctp_intl_store_reasm(struct sctp_ulpq *ulpq,
                return;
        }
 
+       loc = NULL;
        skb_queue_walk(&ulpq->reasm, pos) {
                cevent = sctp_skb2event(pos);
 
                if (event->stream < cevent->stream ||
                    (event->stream == cevent->stream &&
-                    MID_lt(event->mid, cevent->mid)))
+                    MID_lt(event->mid, cevent->mid))) {
+                       loc = pos;
                        break;
-
+               }
                if (event->stream == cevent->stream &&
                    event->mid == cevent->mid &&
                    !(cevent->msg_flags & SCTP_DATA_FIRST_FRAG) &&
                    (event->msg_flags & SCTP_DATA_FIRST_FRAG ||
-                    event->fsn < cevent->fsn))
+                    event->fsn < cevent->fsn)) {
+                       loc = pos;
                        break;
+               }
        }
 
-       __skb_queue_before(&ulpq->reasm, pos, sctp_event2skb(event));
+       if (!loc)
+               __skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
+       else
+               __skb_queue_before(&ulpq->reasm, loc, sctp_event2skb(event));
 }
 
 static struct sctp_ulpevent *sctp_intl_retrieve_partial(
@@ -383,7 +390,7 @@ static void sctp_intl_store_ordered(struct sctp_ulpq *ulpq,
                                    struct sctp_ulpevent *event)
 {
        struct sctp_ulpevent *cevent;
-       struct sk_buff *pos;
+       struct sk_buff *pos, *loc;
 
        pos = skb_peek_tail(&ulpq->lobby);
        if (!pos) {
@@ -403,18 +410,25 @@ static void sctp_intl_store_ordered(struct sctp_ulpq *ulpq,
                return;
        }
 
+       loc = NULL;
        skb_queue_walk(&ulpq->lobby, pos) {
                cevent = (struct sctp_ulpevent *)pos->cb;
 
-               if (cevent->stream > event->stream)
+               if (cevent->stream > event->stream) {
+                       loc = pos;
                        break;
-
+               }
                if (cevent->stream == event->stream &&
-                   MID_lt(event->mid, cevent->mid))
+                   MID_lt(event->mid, cevent->mid)) {
+                       loc = pos;
                        break;
+               }
        }
 
-       __skb_queue_before(&ulpq->lobby, pos, sctp_event2skb(event));
+       if (!loc)
+               __skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
+       else
+               __skb_queue_before(&ulpq->lobby, loc, sctp_event2skb(event));
 }
 
 static void sctp_intl_retrieve_ordered(struct sctp_ulpq *ulpq,
@@ -489,7 +503,7 @@ static int sctp_enqueue_event(struct sctp_ulpq *ulpq,
                sk_incoming_cpu_update(sk);
        }
 
-       if (!sctp_ulpevent_is_enabled(event, &sp->subscribe))
+       if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
                goto out_free;
 
        if (skb_list)
@@ -980,17 +994,19 @@ static void sctp_intl_stream_abort_pd(struct sctp_ulpq *ulpq, __u16 sid,
        struct sock *sk = ulpq->asoc->base.sk;
        struct sctp_ulpevent *ev = NULL;
 
-       if (!sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
-                                       &sctp_sk(sk)->subscribe))
+       if (!sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
+                                       SCTP_PARTIAL_DELIVERY_EVENT))
                return;
 
        ev = sctp_ulpevent_make_pdapi(ulpq->asoc, SCTP_PARTIAL_DELIVERY_ABORTED,
                                      sid, mid, flags, gfp);
        if (ev) {
+               struct sctp_sock *sp = sctp_sk(sk);
+
                __skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
 
-               if (!sctp_sk(sk)->data_ready_signalled) {
-                       sctp_sk(sk)->data_ready_signalled = 1;
+               if (!sp->data_ready_signalled) {
+                       sp->data_ready_signalled = 1;
                        sk->sk_data_ready(sk);
                }
        }
index 331cc73..5dde921 100644 (file)
@@ -219,7 +219,7 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
                sk_incoming_cpu_update(sk);
        }
        /* Check if the user wishes to receive this event.  */
-       if (!sctp_ulpevent_is_enabled(event, &sp->subscribe))
+       if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
                goto out_free;
 
        /* If we are in partial delivery mode, post to the lobby until
@@ -1129,16 +1129,16 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
 void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
 {
        struct sctp_ulpevent *ev = NULL;
-       struct sock *sk;
        struct sctp_sock *sp;
+       struct sock *sk;
 
        if (!ulpq->pd_mode)
                return;
 
        sk = ulpq->asoc->base.sk;
        sp = sctp_sk(sk);
-       if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
-                                      &sctp_sk(sk)->subscribe))
+       if (sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
+                                      SCTP_PARTIAL_DELIVERY_EVENT))
                ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
                                              SCTP_PARTIAL_DELIVERY_ABORTED,
                                              0, 0, 0, gfp);
index 5fbaf19..63f08b4 100644 (file)
@@ -301,14 +301,17 @@ static void smc_copy_sock_settings_to_smc(struct smc_sock *smc)
        smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC);
 }
 
-/* register a new rmb, optionally send confirm_rkey msg to register with peer */
+/* register a new rmb, send confirm_rkey msg to register with peer */
 static int smc_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc,
                       bool conf_rkey)
 {
-       /* register memory region for new rmb */
-       if (smc_wr_reg_send(link, rmb_desc->mr_rx[SMC_SINGLE_LINK])) {
-               rmb_desc->regerr = 1;
-               return -EFAULT;
+       if (!rmb_desc->wr_reg) {
+               /* register memory region for new rmb */
+               if (smc_wr_reg_send(link, rmb_desc->mr_rx[SMC_SINGLE_LINK])) {
+                       rmb_desc->regerr = 1;
+                       return -EFAULT;
+               }
+               rmb_desc->wr_reg = 1;
        }
        if (!conf_rkey)
                return 0;
@@ -337,8 +340,8 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
 
        if (link->llc_confirm_rc)
@@ -365,8 +368,8 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
        }
 
        /* send add link reject message, only one link supported for now */
@@ -535,7 +538,8 @@ static int smc_connect_clc(struct smc_sock *smc, int smc_type,
        if (rc)
                return rc;
        /* receive SMC Accept CLC message */
-       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT);
+       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT,
+                               CLC_WAIT_TIME);
 }
 
 /* setup for RDMA connection of client */
@@ -583,8 +587,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RDYLNK,
                                                 local_contact);
        } else {
-               if (!smc->conn.rmb_desc->reused &&
-                   smc_reg_rmb(link, smc->conn.rmb_desc, true))
+               if (smc_reg_rmb(link, smc->conn.rmb_desc, true))
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_REGRMB,
                                                 local_contact);
        }
@@ -968,8 +971,8 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
 
        if (link->llc_confirm_resp_rc)
@@ -989,8 +992,8 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE);
-               return rc;
+                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
+               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
        }
 
        smc_llc_link_active(link, net->ipv4.sysctl_tcp_keepalive_time);
@@ -1145,10 +1148,8 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, int local_contact)
        struct smc_link *link = &new_smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
        if (local_contact != SMC_FIRST_CONTACT) {
-               if (!new_smc->conn.rmb_desc->reused) {
-                       if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
-                               return SMC_CLC_DECL_ERR_REGRMB;
-               }
+               if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
+                       return SMC_CLC_DECL_ERR_REGRMB;
        }
        smc_rmb_sync_sg_for_device(&new_smc->conn);
 
@@ -1184,7 +1185,6 @@ static int smc_listen_rdma_finish(struct smc_sock *new_smc,
        return 0;
 
 decline:
-       mutex_unlock(&smc_create_lgr_pending);
        smc_listen_decline(new_smc, reason_code, local_contact);
        return reason_code;
 }
@@ -1225,7 +1225,7 @@ static void smc_listen_work(struct work_struct *work)
         */
        pclc = (struct smc_clc_msg_proposal *)&buf;
        reason_code = smc_clc_wait_msg(new_smc, pclc, SMC_CLC_MAX_LEN,
-                                      SMC_CLC_PROPOSAL);
+                                      SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
        if (reason_code) {
                smc_listen_decline(new_smc, reason_code, 0);
                return;
@@ -1275,7 +1275,7 @@ static void smc_listen_work(struct work_struct *work)
 
        /* receive SMC Confirm CLC message */
        reason_code = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
-                                      SMC_CLC_CONFIRM);
+                                      SMC_CLC_CONFIRM, CLC_WAIT_TIME);
        if (reason_code) {
                mutex_unlock(&smc_create_lgr_pending);
                smc_listen_decline(new_smc, reason_code, local_contact);
@@ -1284,8 +1284,10 @@ static void smc_listen_work(struct work_struct *work)
 
        /* finish worker */
        if (!ism_supported) {
-               if (smc_listen_rdma_finish(new_smc, &cclc, local_contact))
+               if (smc_listen_rdma_finish(new_smc, &cclc, local_contact)) {
+                       mutex_unlock(&smc_create_lgr_pending);
                        return;
+               }
        }
        smc_conn_save_peer_info(new_smc, &cclc);
        mutex_unlock(&smc_create_lgr_pending);
@@ -1357,7 +1359,6 @@ static int smc_listen(struct socket *sock, int backlog)
        sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
        sk->sk_state = SMC_LISTEN;
-       INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
        sock_hold(sk); /* sock_hold in tcp_listen_worker */
        if (!schedule_work(&smc->tcp_listen_work))
                sock_put(sk);
index 89c3a8c..776e9df 100644 (file)
@@ -265,7 +265,7 @@ out:
  * clcsock error, -EINTR, -ECONNRESET, -EPROTO otherwise.
  */
 int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
-                    u8 expected_type)
+                    u8 expected_type, unsigned long timeout)
 {
        long rcvtimeo = smc->clcsock->sk->sk_rcvtimeo;
        struct sock *clc_sk = smc->clcsock->sk;
@@ -285,7 +285,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
         * sizeof(struct smc_clc_msg_hdr)
         */
        krflags = MSG_PEEK | MSG_WAITALL;
-       smc->clcsock->sk->sk_rcvtimeo = CLC_WAIT_TIME;
+       clc_sk->sk_rcvtimeo = timeout;
        iov_iter_kvec(&msg.msg_iter, READ, &vec, 1,
                        sizeof(struct smc_clc_msg_hdr));
        len = sock_recvmsg(smc->clcsock, &msg, krflags);
@@ -297,7 +297,11 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
        }
        if (clc_sk->sk_err) {
                reason_code = -clc_sk->sk_err;
-               smc->sk.sk_err = clc_sk->sk_err;
+               if (clc_sk->sk_err == EAGAIN &&
+                   expected_type == SMC_CLC_DECLINE)
+                       clc_sk->sk_err = 0; /* reset for fallback usage */
+               else
+                       smc->sk.sk_err = clc_sk->sk_err;
                goto out;
        }
        if (!len) { /* peer has performed orderly shutdown */
@@ -306,7 +310,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
                goto out;
        }
        if (len < 0) {
-               smc->sk.sk_err = -len;
+               if (len != -EAGAIN || expected_type != SMC_CLC_DECLINE)
+                       smc->sk.sk_err = -len;
                reason_code = len;
                goto out;
        }
@@ -346,7 +351,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
        }
 
 out:
-       smc->clcsock->sk->sk_rcvtimeo = rcvtimeo;
+       clc_sk->sk_rcvtimeo = rcvtimeo;
        return reason_code;
 }
 
@@ -374,10 +379,8 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
        len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1,
                             sizeof(struct smc_clc_msg_decline));
        if (len < sizeof(struct smc_clc_msg_decline))
-               smc->sk.sk_err = EPROTO;
-       if (len < 0)
-               smc->sk.sk_err = -len;
-       return sock_error(&smc->sk);
+               len = -EPROTO;
+       return len > 0 ? 0 : len;
 }
 
 /* send CLC PROPOSAL message across internal TCP socket */
@@ -536,7 +539,6 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
        struct smc_link *link;
        struct msghdr msg;
        struct kvec vec;
-       int rc = 0;
        int len;
 
        memset(&aclc, 0, sizeof(aclc));
@@ -589,13 +591,8 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
        vec.iov_len = ntohs(aclc.hdr.length);
        len = kernel_sendmsg(new_smc->clcsock, &msg, &vec, 1,
                             ntohs(aclc.hdr.length));
-       if (len < ntohs(aclc.hdr.length)) {
-               if (len >= 0)
-                       new_smc->sk.sk_err = EPROTO;
-               else
-                       new_smc->sk.sk_err = new_smc->clcsock->sk->sk_err;
-               rc = sock_error(&new_smc->sk);
-       }
+       if (len < ntohs(aclc.hdr.length))
+               len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err;
 
-       return rc;
+       return len > 0 ? 0 : len;
 }
index 18da89b..24658e8 100644 (file)
@@ -27,6 +27,7 @@
 #define SMC_TYPE_D             1               /* SMC-D only                 */
 #define SMC_TYPE_B             3               /* SMC-R and SMC-D            */
 #define CLC_WAIT_TIME          (6 * HZ)        /* max. wait time on clcsock  */
+#define CLC_WAIT_TIME_SHORT    HZ              /* short wait time on clcsock */
 #define SMC_CLC_DECL_MEM       0x01010000  /* insufficient memory resources  */
 #define SMC_CLC_DECL_TIMEOUT_CL        0x02010000  /* timeout w4 QP confirm link     */
 #define SMC_CLC_DECL_TIMEOUT_AL        0x02020000  /* timeout w4 QP add link         */
@@ -182,7 +183,7 @@ struct smcd_dev;
 int smc_clc_prfx_match(struct socket *clcsock,
                       struct smc_clc_msg_proposal_prefix *prop);
 int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
-                    u8 expected_type);
+                    u8 expected_type, unsigned long timeout);
 int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info);
 int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
                          struct smc_ib_device *smcibdev, u8 ibport, u8 gid[],
index 1c9fa7f..35c1cdc 100644 (file)
@@ -149,6 +149,8 @@ static int smc_link_send_delete(struct smc_link *lnk)
        return -ENOTCONN;
 }
 
+static void smc_lgr_free(struct smc_link_group *lgr);
+
 static void smc_lgr_free_work(struct work_struct *work)
 {
        struct smc_link_group *lgr = container_of(to_delayed_work(work),
@@ -171,8 +173,11 @@ free:
        spin_unlock_bh(&smc_lgr_list.lock);
 
        if (!lgr->is_smcd && !lgr->terminating) {
+               struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
+
                /* try to send del link msg, on error free lgr immediately */
-               if (!smc_link_send_delete(&lgr->lnk[SMC_SINGLE_LINK])) {
+               if (lnk->state == SMC_LNK_ACTIVE &&
+                   !smc_link_send_delete(lnk)) {
                        /* reschedule in case we never receive a response */
                        smc_lgr_schedule_free_work(lgr);
                        return;
@@ -295,8 +300,13 @@ static void smc_buf_unuse(struct smc_connection *conn,
                conn->sndbuf_desc->used = 0;
        if (conn->rmb_desc) {
                if (!conn->rmb_desc->regerr) {
-                       conn->rmb_desc->reused = 1;
                        conn->rmb_desc->used = 0;
+                       if (!lgr->is_smcd) {
+                               /* unregister rmb with peer */
+                               smc_llc_do_delete_rkey(
+                                               &lgr->lnk[SMC_SINGLE_LINK],
+                                               conn->rmb_desc);
+                       }
                } else {
                        /* buf registration failed, reuse not possible */
                        write_lock_bh(&lgr->rmbs_lock);
@@ -410,7 +420,7 @@ static void smc_lgr_free_bufs(struct smc_link_group *lgr)
 }
 
 /* remove a link group */
-void smc_lgr_free(struct smc_link_group *lgr)
+static void smc_lgr_free(struct smc_link_group *lgr)
 {
        smc_lgr_free_bufs(lgr);
        if (lgr->is_smcd)
index cf98f4d..b002879 100644 (file)
@@ -109,6 +109,9 @@ struct smc_link {
        int                     llc_testlink_time; /* testlink interval */
        struct completion       llc_confirm_rkey; /* wait 4 rx of cnf rkey */
        int                     llc_confirm_rkey_rc; /* rc from cnf rkey msg */
+       struct completion       llc_delete_rkey; /* wait 4 rx of del rkey */
+       int                     llc_delete_rkey_rc; /* rc from del rkey msg */
+       struct mutex            llc_delete_rkey_mutex; /* serialize usage */
 };
 
 /* For now we just allow one parallel link per link group. The SMC protocol
@@ -127,7 +130,7 @@ struct smc_buf_desc {
        struct page             *pages;
        int                     len;            /* length of buffer */
        u32                     used;           /* currently used / unused */
-       u8                      reused  : 1;    /* new created / reused */
+       u8                      wr_reg  : 1;    /* mem region registered */
        u8                      regerr  : 1;    /* err during registration */
        union {
                struct { /* SMC-R */
@@ -243,7 +246,6 @@ struct smc_sock;
 struct smc_clc_msg_accept_confirm;
 struct smc_clc_msg_local;
 
-void smc_lgr_free(struct smc_link_group *lgr);
 void smc_lgr_forget(struct smc_link_group *lgr);
 void smc_lgr_terminate(struct smc_link_group *lgr);
 void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport);
index 9c916c7..a6d3623 100644 (file)
@@ -238,6 +238,29 @@ static int smc_llc_send_confirm_rkey(struct smc_link *link,
        return rc;
 }
 
+/* send LLC delete rkey request */
+static int smc_llc_send_delete_rkey(struct smc_link *link,
+                                   struct smc_buf_desc *rmb_desc)
+{
+       struct smc_llc_msg_delete_rkey *rkeyllc;
+       struct smc_wr_tx_pend_priv *pend;
+       struct smc_wr_buf *wr_buf;
+       int rc;
+
+       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
+       if (rc)
+               return rc;
+       rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf;
+       memset(rkeyllc, 0, sizeof(*rkeyllc));
+       rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
+       rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
+       rkeyllc->num_rkeys = 1;
+       rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+       /* send llc message */
+       rc = smc_wr_tx_send(link, pend);
+       return rc;
+}
+
 /* prepare an add link message */
 static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
                                  struct smc_link *link, u8 mac[], u8 gid[],
@@ -509,7 +532,9 @@ static void smc_llc_rx_delete_rkey(struct smc_link *link,
        int i, max;
 
        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               /* unused as long as we don't send this type of msg */
+               link->llc_delete_rkey_rc = llc->hd.flags &
+                                           SMC_LLC_FLAG_RKEY_NEG;
+               complete(&link->llc_delete_rkey);
        } else {
                max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
                for (i = 0; i < max; i++) {
@@ -610,6 +635,8 @@ int smc_llc_link_init(struct smc_link *link)
        init_completion(&link->llc_add);
        init_completion(&link->llc_add_resp);
        init_completion(&link->llc_confirm_rkey);
+       init_completion(&link->llc_delete_rkey);
+       mutex_init(&link->llc_delete_rkey_mutex);
        init_completion(&link->llc_testlink_resp);
        INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
        return 0;
@@ -650,8 +677,11 @@ int smc_llc_do_confirm_rkey(struct smc_link *link,
 {
        int rc;
 
+       /* protected by mutex smc_create_lgr_pending */
        reinit_completion(&link->llc_confirm_rkey);
-       smc_llc_send_confirm_rkey(link, rmb_desc);
+       rc = smc_llc_send_confirm_rkey(link, rmb_desc);
+       if (rc)
+               return rc;
        /* receive CONFIRM RKEY response from server over RoCE fabric */
        rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey,
                                                       SMC_LLC_WAIT_TIME);
@@ -660,6 +690,29 @@ int smc_llc_do_confirm_rkey(struct smc_link *link,
        return 0;
 }
 
+/* unregister an rtoken at the remote peer */
+int smc_llc_do_delete_rkey(struct smc_link *link,
+                          struct smc_buf_desc *rmb_desc)
+{
+       int rc;
+
+       mutex_lock(&link->llc_delete_rkey_mutex);
+       reinit_completion(&link->llc_delete_rkey);
+       rc = smc_llc_send_delete_rkey(link, rmb_desc);
+       if (rc)
+               goto out;
+       /* receive DELETE RKEY response from server over RoCE fabric */
+       rc = wait_for_completion_interruptible_timeout(&link->llc_delete_rkey,
+                                                      SMC_LLC_WAIT_TIME);
+       if (rc <= 0 || link->llc_delete_rkey_rc)
+               rc = -EFAULT;
+       else
+               rc = 0;
+out:
+       mutex_unlock(&link->llc_delete_rkey_mutex);
+       return rc;
+}
+
 /***************************** init, exit, misc ******************************/
 
 static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
index 9e2ff08..461c0c3 100644 (file)
@@ -49,6 +49,8 @@ void smc_llc_link_inactive(struct smc_link *link);
 void smc_llc_link_clear(struct smc_link *link);
 int smc_llc_do_confirm_rkey(struct smc_link *link,
                            struct smc_buf_desc *rmb_desc);
+int smc_llc_do_delete_rkey(struct smc_link *link,
+                          struct smc_buf_desc *rmb_desc);
 int smc_llc_init(void) __init;
 
 #endif /* SMC_LLC_H */
index 9062967..7e55cfc 100644 (file)
@@ -175,7 +175,7 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
                return -1;
        if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
            !skb->csum_complete_sw)
-               netdev_rx_csum_fault(skb->dev);
+               netdev_rx_csum_fault(skb->dev, skb);
        return 0;
 no_checksum:
        if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0)
index 74b9d91..fe23fac 100644 (file)
@@ -353,30 +353,29 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
        return 0;
 }
 
-static int __switchdev_port_obj_add(struct net_device *dev,
-                                   const struct switchdev_obj *obj,
-                                   struct switchdev_trans *trans)
+static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
+                                    struct net_device *dev,
+                                    const struct switchdev_obj *obj,
+                                    struct switchdev_trans *trans)
 {
-       const struct switchdev_ops *ops = dev->switchdev_ops;
-       struct net_device *lower_dev;
-       struct list_head *iter;
-       int err = -EOPNOTSUPP;
-
-       if (ops && ops->switchdev_port_obj_add)
-               return ops->switchdev_port_obj_add(dev, obj, trans);
+       int rc;
+       int err;
 
-       /* Switch device port(s) may be stacked under
-        * bond/team/vlan dev, so recurse down to add object on
-        * each port.
-        */
+       struct switchdev_notifier_port_obj_info obj_info = {
+               .obj = obj,
+               .trans = trans,
+               .handled = false,
+       };
 
-       netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = __switchdev_port_obj_add(lower_dev, obj, trans);
-               if (err)
-                       break;
+       rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info);
+       err = notifier_to_errno(rc);
+       if (err) {
+               WARN_ON(!obj_info.handled);
+               return err;
        }
-
-       return err;
+       if (!obj_info.handled)
+               return -EOPNOTSUPP;
+       return 0;
 }
 
 static int switchdev_port_obj_add_now(struct net_device *dev,
@@ -397,7 +396,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
         */
 
        trans.ph_prepare = true;
-       err = __switchdev_port_obj_add(dev, obj, &trans);
+       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                       dev, obj, &trans);
        if (err) {
                /* Prepare phase failed: abort the transaction.  Any
                 * resources reserved in the prepare phase are
@@ -416,7 +416,8 @@ static int switchdev_port_obj_add_now(struct net_device *dev,
         */
 
        trans.ph_prepare = false;
-       err = __switchdev_port_obj_add(dev, obj, &trans);
+       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                       dev, obj, &trans);
        WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
        switchdev_trans_items_warn_destroy(dev, &trans);
 
@@ -471,26 +472,8 @@ EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
 static int switchdev_port_obj_del_now(struct net_device *dev,
                                      const struct switchdev_obj *obj)
 {
-       const struct switchdev_ops *ops = dev->switchdev_ops;
-       struct net_device *lower_dev;
-       struct list_head *iter;
-       int err = -EOPNOTSUPP;
-
-       if (ops && ops->switchdev_port_obj_del)
-               return ops->switchdev_port_obj_del(dev, obj);
-
-       /* Switch device port(s) may be stacked under
-        * bond/team/vlan dev, so recurse down to delete object on
-        * each port.
-        */
-
-       netdev_for_each_lower_dev(dev, lower_dev, iter) {
-               err = switchdev_port_obj_del_now(lower_dev, obj);
-               if (err)
-                       break;
-       }
-
-       return err;
+       return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
+                                        dev, obj, NULL);
 }
 
 static void switchdev_port_obj_del_deferred(struct net_device *dev,
@@ -535,6 +518,7 @@ int switchdev_port_obj_del(struct net_device *dev,
 EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
 
 static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
+static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain);
 
 /**
  *     register_switchdev_notifier - Register notifier
@@ -576,6 +560,31 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
 
+int register_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
+
+       return blocking_notifier_chain_register(chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier);
+
+int unregister_switchdev_blocking_notifier(struct notifier_block *nb)
+{
+       struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain;
+
+       return blocking_notifier_chain_unregister(chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier);
+
+int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev,
+                                     struct switchdev_notifier_info *info)
+{
+       info->dev = dev;
+       return blocking_notifier_call_chain(&switchdev_blocking_notif_chain,
+                                           val, info);
+}
+EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers);
+
 bool switchdev_port_same_parent_id(struct net_device *a,
                                   struct net_device *b)
 {
@@ -595,3 +604,103 @@ bool switchdev_port_same_parent_id(struct net_device *a,
        return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
+
+static int __switchdev_handle_port_obj_add(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans))
+{
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       if (check_cb(dev)) {
+               /* This flag is only checked if the return value is success. */
+               port_obj_info->handled = true;
+               return add_cb(dev, port_obj_info->obj, port_obj_info->trans);
+       }
+
+       /* Switch ports might be stacked under e.g. a LAG. Ignore the
+        * unsupported devices, another driver might be able to handle them. But
+        * propagate to the callers any hard errors.
+        *
+        * If the driver does its own bookkeeping of stacked ports, it's not
+        * necessary to go through this helper.
+        */
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
+                                                     check_cb, add_cb);
+               if (err && err != -EOPNOTSUPP)
+                       return err;
+       }
+
+       return err;
+}
+
+int switchdev_handle_port_obj_add(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*add_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj,
+                                     struct switchdev_trans *trans))
+{
+       int err;
+
+       err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
+                                             add_cb);
+       if (err == -EOPNOTSUPP)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
+
+static int __switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       struct net_device *lower_dev;
+       struct list_head *iter;
+       int err = -EOPNOTSUPP;
+
+       if (check_cb(dev)) {
+               /* This flag is only checked if the return value is success. */
+               port_obj_info->handled = true;
+               return del_cb(dev, port_obj_info->obj);
+       }
+
+       /* Switch ports might be stacked under e.g. a LAG. Ignore the
+        * unsupported devices, another driver might be able to handle them. But
+        * propagate to the callers any hard errors.
+        *
+        * If the driver does its own bookkeeping of stacked ports, it's not
+        * necessary to go through this helper.
+        */
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
+                                                     check_cb, del_cb);
+               if (err && err != -EOPNOTSUPP)
+                       return err;
+       }
+
+       return err;
+}
+
+int switchdev_handle_port_obj_del(struct net_device *dev,
+                       struct switchdev_notifier_port_obj_info *port_obj_info,
+                       bool (*check_cb)(const struct net_device *dev),
+                       int (*del_cb)(struct net_device *dev,
+                                     const struct switchdev_obj *obj))
+{
+       int err;
+
+       err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
+                                             del_cb);
+       if (err == -EOPNOTSUPP)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
index 836727e..9e265eb 100644 (file)
@@ -105,7 +105,7 @@ struct tipc_stats {
  * @transmitq: queue for sent, non-acked messages
  * @backlogq: queue for messages waiting to be sent
  * @snt_nxt: next sequence number to use for outbound messages
- * @last_retransmitted: sequence number of most recently retransmitted message
+ * @prev_from: sequence number of most previous retransmission request
  * @stale_cnt: counter for number of identical retransmit attempts
  * @stale_limit: time when repeated identical retransmits must force link reset
  * @ackers: # of peers that needs to ack each packet before it can be released
@@ -163,7 +163,7 @@ struct tipc_link {
                u16 limit;
        } backlog[5];
        u16 snd_nxt;
-       u16 last_retransm;
+       u16 prev_from;
        u16 window;
        u16 stale_cnt;
        unsigned long stale_limit;
@@ -186,9 +186,6 @@ struct tipc_link {
        u16 acked;
        struct tipc_link *bc_rcvlink;
        struct tipc_link *bc_sndlink;
-       unsigned long prev_retr;
-       u16 prev_from;
-       u16 prev_to;
        u8 nack_state;
        bool bc_peer_is_up;
 
@@ -210,7 +207,7 @@ enum {
        BC_NACK_SND_SUPPRESS,
 };
 
-#define TIPC_BC_RETR_LIMIT 10   /* [ms] */
+#define TIPC_BC_RETR_LIM msecs_to_jiffies(10)   /* [ms] */
 
 /*
  * Interval between NACKs when packets arrive out of order
@@ -1036,10 +1033,12 @@ static int tipc_link_retrans(struct tipc_link *l, struct tipc_link *r,
 
        if (!skb)
                return 0;
+       if (less(to, from))
+               return 0;
 
        /* Detect repeated retransmit failures on same packet */
-       if (r->last_retransm != buf_seqno(skb)) {
-               r->last_retransm = buf_seqno(skb);
+       if (r->prev_from != from) {
+               r->prev_from = from;
                r->stale_limit = jiffies + msecs_to_jiffies(r->tolerance);
                r->stale_cnt = 0;
        } else if (++r->stale_cnt > 99 && time_after(jiffies, r->stale_limit)) {
@@ -1055,6 +1054,11 @@ static int tipc_link_retrans(struct tipc_link *l, struct tipc_link *r,
                        continue;
                if (more(msg_seqno(hdr), to))
                        break;
+               if (link_is_bc_sndlink(l)) {
+                       if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr))
+                               continue;
+                       TIPC_SKB_CB(skb)->nxt_retr = jiffies + TIPC_BC_RETR_LIM;
+               }
                _skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC);
                if (!_skb)
                        return 0;
@@ -1737,42 +1741,6 @@ void tipc_link_bc_init_rcv(struct tipc_link *l, struct tipc_msg *hdr)
                l->rcv_nxt = peers_snd_nxt;
 }
 
-/* link_bc_retr eval()- check if the indicated range can be retransmitted now
- * - Adjust permitted range if there is overlap with previous retransmission
- */
-static bool link_bc_retr_eval(struct tipc_link *l, u16 *from, u16 *to)
-{
-       unsigned long elapsed = jiffies_to_msecs(jiffies - l->prev_retr);
-
-       if (less(*to, *from))
-               return false;
-
-       /* New retransmission request */
-       if ((elapsed > TIPC_BC_RETR_LIMIT) ||
-           less(*to, l->prev_from) || more(*from, l->prev_to)) {
-               l->prev_from = *from;
-               l->prev_to = *to;
-               l->prev_retr = jiffies;
-               return true;
-       }
-
-       /* Inside range of previous retransmit */
-       if (!less(*from, l->prev_from) && !more(*to, l->prev_to))
-               return false;
-
-       /* Fully or partially outside previous range => exclude overlap */
-       if (less(*from, l->prev_from)) {
-               *to = l->prev_from - 1;
-               l->prev_from = *from;
-       }
-       if (more(*to, l->prev_to)) {
-               *from = l->prev_to + 1;
-               l->prev_to = *to;
-       }
-       l->prev_retr = jiffies;
-       return true;
-}
-
 /* tipc_link_bc_sync_rcv - update rcv link according to peer's send state
  */
 int tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
@@ -1803,8 +1771,7 @@ int tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
        if (more(peers_snd_nxt, l->rcv_nxt + l->window))
                return rc;
 
-       if (link_bc_retr_eval(snd_l, &from, &to))
-               rc = tipc_link_retrans(snd_l, l, from, to, xmitq);
+       rc = tipc_link_retrans(snd_l, l, from, to, xmitq);
 
        l->snd_nxt = peers_snd_nxt;
        if (link_bc_rcv_gap(l))
index a2879e6..a092495 100644 (file)
@@ -105,6 +105,7 @@ struct tipc_skb_cb {
        u32 bytes_read;
        u32 orig_member;
        struct sk_buff *tail;
+       unsigned long nxt_retr;
        bool validated;
        u16 chain_imp;
        u16 ackers;
index 4880197..32556f4 100644 (file)
@@ -624,6 +624,12 @@ static void tipc_node_timeout(struct timer_list *t)
 
        __skb_queue_head_init(&xmitq);
 
+       /* Initial node interval to value larger (10 seconds), then it will be
+        * recalculated with link lowest tolerance
+        */
+       tipc_node_read_lock(n);
+       n->keepalive_intv = 10000;
+       tipc_node_read_unlock(n);
        for (bearer_id = 0; remains && (bearer_id < MAX_BEARERS); bearer_id++) {
                tipc_node_read_lock(n);
                le = &n->links[bearer_id];
index 7b1af8b..d4ecc66 100644 (file)
@@ -687,6 +687,7 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk,
        struct sock *sk_redir;
        struct tls_rec *rec;
        int err = 0, send;
+       u32 delta = 0;
        bool enospc;
 
        psock = sk_psock_get(sk);
@@ -694,8 +695,14 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk,
                return tls_push_record(sk, flags, record_type);
 more_data:
        enospc = sk_msg_full(msg);
-       if (psock->eval == __SK_NONE)
+       if (psock->eval == __SK_NONE) {
+               delta = msg->sg.size;
                psock->eval = sk_psock_msg_verdict(sk, psock, msg);
+               if (delta < msg->sg.size)
+                       delta -= msg->sg.size;
+               else
+                       delta = 0;
+       }
        if (msg->cork_bytes && msg->cork_bytes > msg->sg.size &&
            !enospc && !full_record) {
                err = -ENOSPC;
@@ -743,7 +750,7 @@ more_data:
                        msg->apply_bytes -= send;
                if (msg->sg.size == 0)
                        tls_free_open_rec(sk);
-               *copied -= send;
+               *copied -= (send + delta);
                err = -EACCES;
        }
 
index be0a961..35444f4 100644 (file)
@@ -208,12 +208,20 @@ endif
 BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
 BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
 BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
+BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
+                         $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
+                         readelf -S ./llvm_btf_verify.o | grep BTF; \
+                         /bin/rm -f ./llvm_btf_verify.o)
 
+ifneq ($(BTF_LLVM_PROBE),)
+       EXTRA_CFLAGS += -g
+else
 ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),)
        EXTRA_CFLAGS += -g
        LLC_FLAGS += -mattr=dwarfris
        DWARF2BTF = y
 endif
+endif
 
 # Trick to allow make to be run from this directory
 all:
index e6d7e0f..434ea34 100644 (file)
@@ -54,6 +54,23 @@ static int populate_prog_array(const char *event, int prog_fd)
        return 0;
 }
 
+static int write_kprobe_events(const char *val)
+{
+       int fd, ret, flags;
+
+       if ((val != NULL) && (val[0] == '\0'))
+               flags = O_WRONLY | O_TRUNC;
+       else
+               flags = O_WRONLY | O_APPEND;
+
+       fd = open("/sys/kernel/debug/tracing/kprobe_events", flags);
+
+       ret = write(fd, val, strlen(val));
+       close(fd);
+
+       return ret;
+}
+
 static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 {
        bool is_socket = strncmp(event, "socket", 6) == 0;
@@ -165,10 +182,9 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
 
 #ifdef __x86_64__
                if (strncmp(event, "sys_", 4) == 0) {
-                       snprintf(buf, sizeof(buf),
-                                "echo '%c:__x64_%s __x64_%s' >> /sys/kernel/debug/tracing/kprobe_events",
-                                is_kprobe ? 'p' : 'r', event, event);
-                       err = system(buf);
+                       snprintf(buf, sizeof(buf), "%c:__x64_%s __x64_%s",
+                               is_kprobe ? 'p' : 'r', event, event);
+                       err = write_kprobe_events(buf);
                        if (err >= 0) {
                                need_normal_check = false;
                                event_prefix = "__x64_";
@@ -176,10 +192,9 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
                }
 #endif
                if (need_normal_check) {
-                       snprintf(buf, sizeof(buf),
-                                "echo '%c:%s %s' >> /sys/kernel/debug/tracing/kprobe_events",
-                                is_kprobe ? 'p' : 'r', event, event);
-                       err = system(buf);
+                       snprintf(buf, sizeof(buf), "%c:%s %s",
+                               is_kprobe ? 'p' : 'r', event, event);
+                       err = write_kprobe_events(buf);
                        if (err < 0) {
                                printf("failed to create kprobe '%s' error '%s'\n",
                                       event, strerror(errno));
@@ -284,8 +299,8 @@ static int load_maps(struct bpf_map_data *maps, int nr_maps,
                                                        numa_node);
                }
                if (map_fd[i] < 0) {
-                       printf("failed to create a map: %d %s\n",
-                              errno, strerror(errno));
+                       printf("failed to create map %d (%s): %d %s\n",
+                              i, maps[i].name, errno, strerror(errno));
                        return 1;
                }
                maps[i].fd = map_fd[i];
@@ -519,7 +534,7 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
                return 1;
 
        /* clear all kprobes */
-       i = system("echo \"\" > /sys/kernel/debug/tracing/kprobe_events");
+       i = write_kprobe_events("");
 
        /* scan over all elf sections to get license and map info */
        for (i = 1; i < ehdr.e_shnum; i++) {
index 7bb787c..5318dcb 100644 (file)
@@ -42,7 +42,8 @@ MAP COMMANDS
 |              | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
 |              | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
 |              | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
-|              | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** }
+|              | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
+|              | **queue** | **stack** }
 
 DESCRIPTION
 ===========
index ecf6188..ab36e92 100644 (file)
@@ -15,7 +15,8 @@ SYNOPSIS
        *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
 
        *COMMANDS* :=
-       { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** | **help** }
+       { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load**
+       | **loadall** | **help** }
 
 MAP COMMANDS
 =============
@@ -24,9 +25,9 @@ MAP COMMANDS
 |      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
 |      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |      **bpftool** **prog pin** *PROG* *FILE*
-|      **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-|       **bpftool** **prog attach** *PROG* *ATTACH_TYPE* *MAP*
-|       **bpftool** **prog detach** *PROG* *ATTACH_TYPE* *MAP*
+|      **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
+|       **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+|       **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 |      **bpftool** **prog help**
 |
 |      *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -39,7 +40,9 @@ MAP COMMANDS
 |              **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
 |              **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6**
 |      }
-|       *ATTACH_TYPE* := { **msg_verdict** | **skb_verdict** | **skb_parse** }
+|       *ATTACH_TYPE* := {
+|              **msg_verdict** | **skb_verdict** | **skb_parse** | **flow_dissector**
+|      }
 
 
 DESCRIPTION
@@ -79,8 +82,11 @@ DESCRIPTION
                  contain a dot character ('.'), which is reserved for future
                  extensions of *bpffs*.
 
-       **bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-                 Load bpf program from binary *OBJ* and pin as *FILE*.
+       **bpftool prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*]
+                 Load bpf program(s) from binary *OBJ* and pin as *PATH*.
+                 **bpftool prog load** pins only the first program from the
+                 *OBJ* as *PATH*. **bpftool prog loadall** pins all programs
+                 from the *OBJ* under *PATH* directory.
                  **type** is optional, if not specified program type will be
                  inferred from section names.
                  By default bpftool will create new maps as declared in the ELF
@@ -92,18 +98,24 @@ DESCRIPTION
                  use, referring to it by **id** or through a **pinned** file.
                  If **dev** *NAME* is specified program will be loaded onto
                  given networking device (offload).
+                 Optional **pinmaps** argument can be provided to pin all
+                 maps under *MAP_DIR* directory.
 
-                 Note: *FILE* must be located in *bpffs* mount. It must not
+                 Note: *PATH* must be located in *bpffs* mount. It must not
                  contain a dot character ('.'), which is reserved for future
                  extensions of *bpffs*.
 
-        **bpftool prog attach** *PROG* *ATTACH_TYPE* *MAP*
-                  Attach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
-                  to the map *MAP*.
-
-        **bpftool prog detach** *PROG* *ATTACH_TYPE* *MAP*
-                  Detach bpf program *PROG* (with type specified by *ATTACH_TYPE*)
-                  from the map *MAP*.
+       **bpftool prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+                 Attach bpf program *PROG* (with type specified by
+                 *ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
+                 parameter, with the exception of *flow_dissector* which is
+                 attached to current networking name space.
+
+       **bpftool prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
+                 Detach bpf program *PROG* (with type specified by
+                 *ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
+                 parameter, with the exception of *flow_dissector* which is
+                 detached from the current networking name space.
 
        **bpftool prog help**
                  Print short help message.
index dac7eff..1bea6b9 100644 (file)
@@ -53,7 +53,7 @@ ifneq ($(EXTRA_LDFLAGS),)
 LDFLAGS += $(EXTRA_LDFLAGS)
 endif
 
-LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
+LIBS = -lelf $(LIBBPF)
 
 INSTALL ?= install
 RM ?= rm -f
@@ -90,7 +90,16 @@ include $(wildcard $(OUTPUT)*.d)
 
 all: $(OUTPUT)bpftool
 
-SRCS = $(wildcard *.c)
+BFD_SRCS = jit_disasm.c
+
+SRCS = $(filter-out $(BFD_SRCS),$(wildcard *.c))
+
+ifeq ($(feature-libbfd),1)
+CFLAGS += -DHAVE_LIBBFD_SUPPORT
+SRCS += $(BFD_SRCS)
+LIBS += -lbfd -lopcodes
+endif
+
 OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
 
 $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
index 3f78e64..45c2db2 100644 (file)
@@ -243,7 +243,7 @@ _bpftool()
     # Completion depends on object and command in use
     case $object in
         prog)
-            if [[ $command != "load" ]]; then
+            if [[ $command != "load" && $command != "loadall" ]]; then
                 case $prev in
                     id)
                         _bpftool_get_prog_ids
@@ -299,7 +299,8 @@ _bpftool()
                     fi
 
                     if [[ ${#words[@]} == 6 ]]; then
-                        COMPREPLY=( $( compgen -W "msg_verdict skb_verdict skb_parse" -- "$cur" ) )
+                        COMPREPLY=( $( compgen -W "msg_verdict skb_verdict \
+                            skb_parse flow_dissector" -- "$cur" ) )
                         return 0
                     fi
 
@@ -309,7 +310,7 @@ _bpftool()
                     fi
                     return 0
                     ;;
-                load)
+                load|loadall)
                     local obj
 
                     if [[ ${#words[@]} -lt 6 ]]; then
@@ -338,7 +339,16 @@ _bpftool()
 
                     case $prev in
                         type)
-                            COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \
+                            COMPREPLY=( $( compgen -W "socket kprobe \
+                                kretprobe classifier flow_dissector \
+                                action tracepoint raw_tracepoint \
+                                xdp perf_event cgroup/skb cgroup/sock \
+                                cgroup/dev lwt_in lwt_out lwt_xmit \
+                                lwt_seg6local sockops sk_skb sk_msg \
+                                lirc_mode2 cgroup/bind4 cgroup/bind6 \
+                                cgroup/connect4 cgroup/connect6 \
+                                cgroup/sendmsg4 cgroup/sendmsg6 \
+                                cgroup/post_bind4 cgroup/post_bind6" -- \
                                                    "$cur" ) )
                             return 0
                             ;;
@@ -346,7 +356,7 @@ _bpftool()
                             _bpftool_get_map_ids
                             return 0
                             ;;
-                        pinned)
+                        pinned|pinmaps)
                             _filedir
                             return 0
                             ;;
@@ -358,6 +368,7 @@ _bpftool()
                             COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
                             _bpftool_once_attr 'type'
                             _bpftool_once_attr 'dev'
+                            _bpftool_once_attr 'pinmaps'
                             return 0
                             ;;
                     esac
index e4e6e2b..0f1df90 100644 (file)
@@ -249,3 +249,139 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
 {
        return btf_dumper_do_type(d, type_id, 0, data);
 }
+
+#define BTF_PRINT_ARG(...)                                             \
+       do {                                                            \
+               pos += snprintf(func_sig + pos, size - pos,             \
+                               __VA_ARGS__);                           \
+               if (pos >= size)                                        \
+                       return -1;                                      \
+       } while (0)
+#define BTF_PRINT_TYPE(type)                                   \
+       do {                                                            \
+               pos = __btf_dumper_type_only(btf, type, func_sig,       \
+                                            pos, size);                \
+               if (pos == -1)                                          \
+                       return -1;                                      \
+       } while (0)
+
+static int btf_dump_func(const struct btf *btf, char *func_sig,
+                        const struct btf_type *func_proto,
+                        const struct btf_type *func, int pos, int size);
+
+static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
+                                 char *func_sig, int pos, int size)
+{
+       const struct btf_type *proto_type;
+       const struct btf_array *array;
+       const struct btf_type *t;
+
+       if (!type_id) {
+               BTF_PRINT_ARG("void ");
+               return pos;
+       }
+
+       t = btf__type_by_id(btf, type_id);
+
+       switch (BTF_INFO_KIND(t->info)) {
+       case BTF_KIND_INT:
+               BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_STRUCT:
+               BTF_PRINT_ARG("struct %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_UNION:
+               BTF_PRINT_ARG("union %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_ENUM:
+               BTF_PRINT_ARG("enum %s ",
+                             btf__name_by_offset(btf, t->name_off));
+               break;
+       case BTF_KIND_ARRAY:
+               array = (struct btf_array *)(t + 1);
+               BTF_PRINT_TYPE(array->type);
+               BTF_PRINT_ARG("[%d]", array->nelems);
+               break;
+       case BTF_KIND_PTR:
+               BTF_PRINT_TYPE(t->type);
+               BTF_PRINT_ARG("* ");
+               break;
+       case BTF_KIND_UNKN:
+       case BTF_KIND_FWD:
+       case BTF_KIND_TYPEDEF:
+               return -1;
+       case BTF_KIND_VOLATILE:
+               BTF_PRINT_ARG("volatile ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_CONST:
+               BTF_PRINT_ARG("const ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_RESTRICT:
+               BTF_PRINT_ARG("restrict ");
+               BTF_PRINT_TYPE(t->type);
+               break;
+       case BTF_KIND_FUNC_PROTO:
+               pos = btf_dump_func(btf, func_sig, t, NULL, pos, size);
+               if (pos == -1)
+                       return -1;
+               break;
+       case BTF_KIND_FUNC:
+               proto_type = btf__type_by_id(btf, t->type);
+               pos = btf_dump_func(btf, func_sig, proto_type, t, pos, size);
+               if (pos == -1)
+                       return -1;
+               break;
+       default:
+               return -1;
+       }
+
+       return pos;
+}
+
+static int btf_dump_func(const struct btf *btf, char *func_sig,
+                        const struct btf_type *func_proto,
+                        const struct btf_type *func, int pos, int size)
+{
+       int i, vlen;
+
+       BTF_PRINT_TYPE(func_proto->type);
+       if (func)
+               BTF_PRINT_ARG("%s(", btf__name_by_offset(btf, func->name_off));
+       else
+               BTF_PRINT_ARG("(");
+       vlen = BTF_INFO_VLEN(func_proto->info);
+       for (i = 0; i < vlen; i++) {
+               struct btf_param *arg = &((struct btf_param *)(func_proto + 1))[i];
+
+               if (i)
+                       BTF_PRINT_ARG(", ");
+               if (arg->type) {
+                       BTF_PRINT_TYPE(arg->type);
+                       BTF_PRINT_ARG("%s",
+                                     btf__name_by_offset(btf, arg->name_off));
+               } else {
+                       BTF_PRINT_ARG("...");
+               }
+       }
+       BTF_PRINT_ARG(")");
+
+       return pos;
+}
+
+void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig,
+                         int size)
+{
+       int err;
+
+       func_sig[0] = '\0';
+       if (!btf)
+               return;
+
+       err = __btf_dumper_type_only(btf, type_id, func_sig, 0, size);
+       if (err < 0)
+               func_sig[0] = '\0';
+}
index 70fd48d..4e217d5 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/magic.h>
 #include <net/if.h>
 #include <sys/mount.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
@@ -99,6 +100,13 @@ static bool is_bpffs(char *path)
        return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
 }
 
+void set_max_rlimit(void)
+{
+       struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
+
+       setrlimit(RLIMIT_MEMLOCK, &rinf);
+}
+
 static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
 {
        bool bind_done = false;
@@ -170,34 +178,23 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
        return fd;
 }
 
-int do_pin_fd(int fd, const char *name)
+int mount_bpffs_for_pin(const char *name)
 {
        char err_str[ERR_MAX_LEN];
        char *file;
        char *dir;
        int err = 0;
 
-       err = bpf_obj_pin(fd, name);
-       if (!err)
-               goto out;
-
        file = malloc(strlen(name) + 1);
        strcpy(file, name);
        dir = dirname(file);
 
-       if (errno != EPERM || is_bpffs(dir)) {
-               p_err("can't pin the object (%s): %s", name, strerror(errno));
+       if (is_bpffs(dir))
+               /* nothing to do if already mounted */
                goto out_free;
-       }
 
-       /* Attempt to mount bpffs, then retry pinning. */
        err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
-       if (!err) {
-               err = bpf_obj_pin(fd, name);
-               if (err)
-                       p_err("can't pin the object (%s): %s", name,
-                             strerror(errno));
-       } else {
+       if (err) {
                err_str[ERR_MAX_LEN - 1] = '\0';
                p_err("can't mount BPF file system to pin the object (%s): %s",
                      name, err_str);
@@ -205,10 +202,20 @@ int do_pin_fd(int fd, const char *name)
 
 out_free:
        free(file);
-out:
        return err;
 }
 
+int do_pin_fd(int fd, const char *name)
+{
+       int err;
+
+       err = mount_bpffs_for_pin(name);
+       if (err)
+               return err;
+
+       return bpf_obj_pin(fd, name);
+}
+
 int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
 {
        unsigned int id;
index c75ffd9..b2ed5ee 100644 (file)
@@ -109,7 +109,7 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
                if (inf) {
                        bfdf->arch_info = inf;
                } else {
-                       p_err("No libfd support for %s", arch);
+                       p_err("No libbfd support for %s", arch);
                        return;
                }
        }
@@ -183,3 +183,9 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
 
        bfd_close(bfdf);
 }
+
+int disasm_init(void)
+{
+       bfd_init();
+       return 0;
+}
index 75a3296..5c4c1cd 100644 (file)
@@ -31,7 +31,6 @@
  * SOFTWARE.
  */
 
-#include <bfd.h>
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
@@ -399,8 +398,6 @@ int main(int argc, char **argv)
        if (argc < 0)
                usage();
 
-       bfd_init();
-
        ret = cmd_select(cmds, argc, argv, do_help);
 
        if (json_output)
index a8bf1e2..7431669 100644 (file)
@@ -100,6 +100,8 @@ bool is_prefix(const char *pfx, const char *str);
 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
 void usage(void) __noreturn;
 
+void set_max_rlimit(void);
+
 struct pinned_obj_table {
        DECLARE_HASHTABLE(table, 16);
 };
@@ -129,6 +131,7 @@ const char *get_fd_type_name(enum bpf_obj_type type);
 char *get_fdinfo(int fd, const char *key);
 int open_obj_pinned(char *path, bool quiet);
 int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
+int mount_bpffs_for_pin(const char *name);
 int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
 int do_pin_fd(int fd, const char *name);
 
@@ -144,8 +147,22 @@ int prog_parse_fd(int *argc, char ***argv);
 int map_parse_fd(int *argc, char ***argv);
 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
 
+#ifdef HAVE_LIBBFD_SUPPORT
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
                       const char *arch, const char *disassembler_options);
+int disasm_init(void);
+#else
+static inline
+void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
+                      const char *arch, const char *disassembler_options)
+{
+}
+static inline int disasm_init(void)
+{
+       p_err("No libbfd support");
+       return -1;
+}
+#endif
 void print_data_json(uint8_t *data, size_t len);
 void print_hex_data_json(uint8_t *data, size_t len);
 
@@ -170,6 +187,8 @@ struct btf_dumper {
  */
 int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
                    const void *data);
+void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id,
+                         char *func_only, int size);
 
 struct nlattr;
 struct ifinfomsg;
index 7bf38f0..3850f8d 100644 (file)
 #include "main.h"
 
 static const char * const map_type_name[] = {
-       [BPF_MAP_TYPE_UNSPEC]           = "unspec",
-       [BPF_MAP_TYPE_HASH]             = "hash",
-       [BPF_MAP_TYPE_ARRAY]            = "array",
-       [BPF_MAP_TYPE_PROG_ARRAY]       = "prog_array",
-       [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array",
-       [BPF_MAP_TYPE_PERCPU_HASH]      = "percpu_hash",
-       [BPF_MAP_TYPE_PERCPU_ARRAY]     = "percpu_array",
-       [BPF_MAP_TYPE_STACK_TRACE]      = "stack_trace",
-       [BPF_MAP_TYPE_CGROUP_ARRAY]     = "cgroup_array",
-       [BPF_MAP_TYPE_LRU_HASH]         = "lru_hash",
-       [BPF_MAP_TYPE_LRU_PERCPU_HASH]  = "lru_percpu_hash",
-       [BPF_MAP_TYPE_LPM_TRIE]         = "lpm_trie",
-       [BPF_MAP_TYPE_ARRAY_OF_MAPS]    = "array_of_maps",
-       [BPF_MAP_TYPE_HASH_OF_MAPS]     = "hash_of_maps",
-       [BPF_MAP_TYPE_DEVMAP]           = "devmap",
-       [BPF_MAP_TYPE_SOCKMAP]          = "sockmap",
-       [BPF_MAP_TYPE_CPUMAP]           = "cpumap",
-       [BPF_MAP_TYPE_XSKMAP]           = "xskmap",
-       [BPF_MAP_TYPE_SOCKHASH]         = "sockhash",
-       [BPF_MAP_TYPE_CGROUP_STORAGE]   = "cgroup_storage",
-       [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray",
+       [BPF_MAP_TYPE_UNSPEC]                   = "unspec",
+       [BPF_MAP_TYPE_HASH]                     = "hash",
+       [BPF_MAP_TYPE_ARRAY]                    = "array",
+       [BPF_MAP_TYPE_PROG_ARRAY]               = "prog_array",
+       [BPF_MAP_TYPE_PERF_EVENT_ARRAY]         = "perf_event_array",
+       [BPF_MAP_TYPE_PERCPU_HASH]              = "percpu_hash",
+       [BPF_MAP_TYPE_PERCPU_ARRAY]             = "percpu_array",
+       [BPF_MAP_TYPE_STACK_TRACE]              = "stack_trace",
+       [BPF_MAP_TYPE_CGROUP_ARRAY]             = "cgroup_array",
+       [BPF_MAP_TYPE_LRU_HASH]                 = "lru_hash",
+       [BPF_MAP_TYPE_LRU_PERCPU_HASH]          = "lru_percpu_hash",
+       [BPF_MAP_TYPE_LPM_TRIE]                 = "lpm_trie",
+       [BPF_MAP_TYPE_ARRAY_OF_MAPS]            = "array_of_maps",
+       [BPF_MAP_TYPE_HASH_OF_MAPS]             = "hash_of_maps",
+       [BPF_MAP_TYPE_DEVMAP]                   = "devmap",
+       [BPF_MAP_TYPE_SOCKMAP]                  = "sockmap",
+       [BPF_MAP_TYPE_CPUMAP]                   = "cpumap",
+       [BPF_MAP_TYPE_XSKMAP]                   = "xskmap",
+       [BPF_MAP_TYPE_SOCKHASH]                 = "sockhash",
+       [BPF_MAP_TYPE_CGROUP_STORAGE]           = "cgroup_storage",
+       [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY]      = "reuseport_sockarray",
        [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE]    = "percpu_cgroup_storage",
+       [BPF_MAP_TYPE_QUEUE]                    = "queue",
+       [BPF_MAP_TYPE_STACK]                    = "stack",
 };
 
 static bool map_is_per_cpu(__u32 type)
@@ -215,70 +217,6 @@ err_end_obj:
        return ret;
 }
 
-static int get_btf(struct bpf_map_info *map_info, struct btf **btf)
-{
-       struct bpf_btf_info btf_info = { 0 };
-       __u32 len = sizeof(btf_info);
-       __u32 last_size;
-       int btf_fd;
-       void *ptr;
-       int err;
-
-       err = 0;
-       *btf = NULL;
-       btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
-       if (btf_fd < 0)
-               return 0;
-
-       /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
-        * let's start with a sane default - 4KiB here - and resize it only if
-        * bpf_obj_get_info_by_fd() needs a bigger buffer.
-        */
-       btf_info.btf_size = 4096;
-       last_size = btf_info.btf_size;
-       ptr = malloc(last_size);
-       if (!ptr) {
-               err = -ENOMEM;
-               goto exit_free;
-       }
-
-       bzero(ptr, last_size);
-       btf_info.btf = ptr_to_u64(ptr);
-       err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
-
-       if (!err && btf_info.btf_size > last_size) {
-               void *temp_ptr;
-
-               last_size = btf_info.btf_size;
-               temp_ptr = realloc(ptr, last_size);
-               if (!temp_ptr) {
-                       err = -ENOMEM;
-                       goto exit_free;
-               }
-               ptr = temp_ptr;
-               bzero(ptr, last_size);
-               btf_info.btf = ptr_to_u64(ptr);
-               err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
-       }
-
-       if (err || btf_info.btf_size > last_size) {
-               err = errno;
-               goto exit_free;
-       }
-
-       *btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL);
-       if (IS_ERR(*btf)) {
-               err = PTR_ERR(*btf);
-               *btf = NULL;
-       }
-
-exit_free:
-       close(btf_fd);
-       free(ptr);
-
-       return err;
-}
-
 static json_writer_t *get_btf_writer(void)
 {
        json_writer_t *jw = jsonw_new(stdout);
@@ -383,7 +321,10 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
                printf(single_line ? "  " : "\n");
 
                printf("value:%c", break_names ? '\n' : ' ');
-               fprint_hex(stdout, value, info->value_size, " ");
+               if (value)
+                       fprint_hex(stdout, value, info->value_size, " ");
+               else
+                       printf("<no entry>");
 
                printf("\n");
        } else {
@@ -398,8 +339,11 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
                for (i = 0; i < n; i++) {
                        printf("value (CPU %02d):%c",
                               i, info->value_size > 16 ? '\n' : ' ');
-                       fprint_hex(stdout, value + i * step,
-                                  info->value_size, " ");
+                       if (value)
+                               fprint_hex(stdout, value + i * step,
+                                          info->value_size, " ");
+                       else
+                               printf("<no entry>");
                        printf("\n");
                }
        }
@@ -731,7 +675,11 @@ static int dump_map_elem(int fd, void *key, void *value,
                jsonw_string_field(json_wtr, "error", strerror(lookup_errno));
                jsonw_end_object(json_wtr);
        } else {
-               print_entry_error(map_info, key, strerror(lookup_errno));
+               if (errno == ENOENT)
+                       print_entry_plain(map_info, key, NULL);
+               else
+                       print_entry_error(map_info, key,
+                                         strerror(lookup_errno));
        }
 
        return 0;
@@ -765,7 +713,7 @@ static int do_dump(int argc, char **argv)
 
        prev_key = NULL;
 
-       err = get_btf(&info, &btf);
+       err = btf__get_from_id(info.btf_id, &btf);
        if (err) {
                p_err("failed to get btf");
                goto exit_free;
@@ -909,7 +857,7 @@ static int do_lookup(int argc, char **argv)
        }
 
        /* here means bpf_map_lookup_elem() succeeded */
-       err = get_btf(&info, &btf);
+       err = btf__get_from_id(info.btf_id, &btf);
        if (err) {
                p_err("failed to get btf");
                goto exit_free;
@@ -1140,6 +1088,8 @@ static int do_create(int argc, char **argv)
                return -1;
        }
 
+       set_max_rlimit();
+
        fd = bpf_create_map_xattr(&attr);
        if (fd < 0) {
                p_err("map create failed: %s", strerror(errno));
index ccee180..4e04699 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/err.h>
 
 #include <bpf.h>
+#include <btf.h>
 #include <libbpf.h>
 
 #include "cfg.h"
@@ -81,6 +82,7 @@ static const char * const attach_type_strings[] = {
        [BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
        [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
        [BPF_SK_MSG_VERDICT] = "msg_verdict",
+       [BPF_FLOW_DISSECTOR] = "flow_dissector",
        [__MAX_BPF_ATTACH_TYPE] = NULL,
 };
 
@@ -449,14 +451,19 @@ static int do_dump(int argc, char **argv)
        struct bpf_prog_info info = {};
        unsigned int *func_lens = NULL;
        const char *disasm_opt = NULL;
+       unsigned int finfo_rec_size;
        unsigned int nr_func_ksyms;
        unsigned int nr_func_lens;
        struct dump_data dd = {};
        __u32 len = sizeof(info);
+       struct btf *btf = NULL;
+       void *func_info = NULL;
+       unsigned int finfo_cnt;
        unsigned int buf_size;
        char *filepath = NULL;
        bool opcodes = false;
        bool visual = false;
+       char func_sig[1024];
        unsigned char *buf;
        __u32 *member_len;
        __u64 *member_ptr;
@@ -465,6 +472,9 @@ static int do_dump(int argc, char **argv)
        int fd;
 
        if (is_prefix(*argv, "jited")) {
+               if (disasm_init())
+                       return -1;
+
                member_len = &info.jited_prog_len;
                member_ptr = &info.jited_prog_insns;
        } else if (is_prefix(*argv, "xlated")) {
@@ -546,6 +556,17 @@ static int do_dump(int argc, char **argv)
                }
        }
 
+       finfo_cnt = info.func_info_cnt;
+       finfo_rec_size = info.func_info_rec_size;
+       if (finfo_cnt && finfo_rec_size) {
+               func_info = malloc(finfo_cnt * finfo_rec_size);
+               if (!func_info) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
        memset(&info, 0, sizeof(info));
 
        *member_ptr = ptr_to_u64(buf);
@@ -554,6 +575,9 @@ static int do_dump(int argc, char **argv)
        info.nr_jited_ksyms = nr_func_ksyms;
        info.jited_func_lens = ptr_to_u64(func_lens);
        info.nr_jited_func_lens = nr_func_lens;
+       info.func_info_cnt = finfo_cnt;
+       info.func_info_rec_size = finfo_rec_size;
+       info.func_info = ptr_to_u64(func_info);
 
        err = bpf_obj_get_info_by_fd(fd, &info, &len);
        close(fd);
@@ -577,6 +601,18 @@ static int do_dump(int argc, char **argv)
                goto err_free;
        }
 
+       if (info.func_info_cnt != finfo_cnt) {
+               p_err("incorrect func_info_cnt %d vs. expected %d",
+                     info.func_info_cnt, finfo_cnt);
+               goto err_free;
+       }
+
+       if (info.func_info_rec_size != finfo_rec_size) {
+               p_err("incorrect func_info_rec_size %d vs. expected %d",
+                     info.func_info_rec_size, finfo_rec_size);
+               goto err_free;
+       }
+
        if ((member_len == &info.jited_prog_len &&
             info.jited_prog_insns == 0) ||
            (member_len == &info.xlated_prog_len &&
@@ -585,6 +621,11 @@ static int do_dump(int argc, char **argv)
                goto err_free;
        }
 
+       if (info.btf_id && btf__get_from_id(info.btf_id, &btf)) {
+               p_err("failed to get btf");
+               goto err_free;
+       }
+
        if (filepath) {
                fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
                if (fd < 0) {
@@ -617,6 +658,7 @@ static int do_dump(int argc, char **argv)
 
                if (info.nr_jited_func_lens && info.jited_func_lens) {
                        struct kernel_sym *sym = NULL;
+                       struct bpf_func_info *record;
                        char sym_name[SYM_MAX_NAME];
                        unsigned char *img = buf;
                        __u64 *ksyms = NULL;
@@ -643,12 +685,25 @@ static int do_dump(int argc, char **argv)
                                        strcpy(sym_name, "unknown");
                                }
 
+                               if (func_info) {
+                                       record = func_info + i * finfo_rec_size;
+                                       btf_dumper_type_only(btf, record->type_id,
+                                                            func_sig,
+                                                            sizeof(func_sig));
+                               }
+
                                if (json_output) {
                                        jsonw_start_object(json_wtr);
+                                       if (func_info && func_sig[0] != '\0') {
+                                               jsonw_name(json_wtr, "proto");
+                                               jsonw_string(json_wtr, func_sig);
+                                       }
                                        jsonw_name(json_wtr, "name");
                                        jsonw_string(json_wtr, sym_name);
                                        jsonw_name(json_wtr, "insns");
                                } else {
+                                       if (func_info && func_sig[0] != '\0')
+                                               printf("%s:\n", func_sig);
                                        printf("%s:\n", sym_name);
                                }
 
@@ -677,6 +732,9 @@ static int do_dump(int argc, char **argv)
                kernel_syms_load(&dd);
                dd.nr_jited_ksyms = info.nr_jited_ksyms;
                dd.jited_ksyms = (__u64 *) info.jited_ksyms;
+               dd.btf = btf;
+               dd.func_info = func_info;
+               dd.finfo_rec_size = finfo_rec_size;
 
                if (json_output)
                        dump_xlated_json(&dd, buf, *member_len, opcodes);
@@ -688,12 +746,14 @@ static int do_dump(int argc, char **argv)
        free(buf);
        free(func_ksyms);
        free(func_lens);
+       free(func_info);
        return 0;
 
 err_free:
        free(buf);
        free(func_ksyms);
        free(func_lens);
+       free(func_info);
        return -1;
 }
 
@@ -720,30 +780,49 @@ int map_replace_compar(const void *p1, const void *p2)
        return a->idx - b->idx;
 }
 
-static int do_attach(int argc, char **argv)
+static int parse_attach_detach_args(int argc, char **argv, int *progfd,
+                                   enum bpf_attach_type *attach_type,
+                                   int *mapfd)
 {
-       enum bpf_attach_type attach_type;
-       int err, mapfd, progfd;
-
-       if (!REQ_ARGS(5)) {
-               p_err("too few parameters for map attach");
+       if (!REQ_ARGS(3))
                return -EINVAL;
-       }
 
-       progfd = prog_parse_fd(&argc, &argv);
-       if (progfd < 0)
-               return progfd;
+       *progfd = prog_parse_fd(&argc, &argv);
+       if (*progfd < 0)
+               return *progfd;
 
-       attach_type = parse_attach_type(*argv);
-       if (attach_type == __MAX_BPF_ATTACH_TYPE) {
-               p_err("invalid attach type");
+       *attach_type = parse_attach_type(*argv);
+       if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
+               p_err("invalid attach/detach type");
                return -EINVAL;
        }
+
+       if (*attach_type == BPF_FLOW_DISSECTOR) {
+               *mapfd = -1;
+               return 0;
+       }
+
        NEXT_ARG();
+       if (!REQ_ARGS(2))
+               return -EINVAL;
 
-       mapfd = map_parse_fd(&argc, &argv);
-       if (mapfd < 0)
-               return mapfd;
+       *mapfd = map_parse_fd(&argc, &argv);
+       if (*mapfd < 0)
+               return *mapfd;
+
+       return 0;
+}
+
+static int do_attach(int argc, char **argv)
+{
+       enum bpf_attach_type attach_type;
+       int err, progfd;
+       int mapfd;
+
+       err = parse_attach_detach_args(argc, argv,
+                                      &progfd, &attach_type, &mapfd);
+       if (err)
+               return err;
 
        err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
        if (err) {
@@ -759,27 +838,13 @@ static int do_attach(int argc, char **argv)
 static int do_detach(int argc, char **argv)
 {
        enum bpf_attach_type attach_type;
-       int err, mapfd, progfd;
-
-       if (!REQ_ARGS(5)) {
-               p_err("too few parameters for map detach");
-               return -EINVAL;
-       }
+       int err, progfd;
+       int mapfd;
 
-       progfd = prog_parse_fd(&argc, &argv);
-       if (progfd < 0)
-               return progfd;
-
-       attach_type = parse_attach_type(*argv);
-       if (attach_type == __MAX_BPF_ATTACH_TYPE) {
-               p_err("invalid attach type");
-               return -EINVAL;
-       }
-       NEXT_ARG();
-
-       mapfd = map_parse_fd(&argc, &argv);
-       if (mapfd < 0)
-               return mapfd;
+       err = parse_attach_detach_args(argc, argv,
+                                      &progfd, &attach_type, &mapfd);
+       if (err)
+               return err;
 
        err = bpf_prog_detach2(progfd, mapfd, attach_type);
        if (err) {
@@ -791,15 +856,17 @@ static int do_detach(int argc, char **argv)
                jsonw_null(json_wtr);
        return 0;
 }
-static int do_load(int argc, char **argv)
+
+static int load_with_options(int argc, char **argv, bool first_prog_only)
 {
        enum bpf_attach_type expected_attach_type;
        struct bpf_object_open_attr attr = {
                .prog_type      = BPF_PROG_TYPE_UNSPEC,
        };
        struct map_replace *map_replace = NULL;
+       struct bpf_program *prog = NULL, *pos;
        unsigned int old_map_fds = 0;
-       struct bpf_program *prog;
+       const char *pinmaps = NULL;
        struct bpf_object *obj;
        struct bpf_map *map;
        const char *pinfile;
@@ -908,6 +975,13 @@ static int do_load(int argc, char **argv)
                                goto err_free_reuse_maps;
                        }
                        NEXT_ARG();
+               } else if (is_prefix(*argv, "pinmaps")) {
+                       NEXT_ARG();
+
+                       if (!REQ_ARGS(1))
+                               goto err_free_reuse_maps;
+
+                       pinmaps = GET_ARG();
                } else {
                        p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
                              *argv);
@@ -921,26 +995,25 @@ static int do_load(int argc, char **argv)
                goto err_free_reuse_maps;
        }
 
-       prog = bpf_program__next(NULL, obj);
-       if (!prog) {
-               p_err("object file doesn't contain any bpf program");
-               goto err_close_obj;
-       }
+       bpf_object__for_each_program(pos, obj) {
+               enum bpf_prog_type prog_type = attr.prog_type;
 
-       bpf_program__set_ifindex(prog, ifindex);
-       if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
-               const char *sec_name = bpf_program__title(prog, false);
+               if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
+                       const char *sec_name = bpf_program__title(pos, false);
 
-               err = libbpf_prog_type_by_name(sec_name, &attr.prog_type,
-                                              &expected_attach_type);
-               if (err < 0) {
-                       p_err("failed to guess program type based on section name %s\n",
-                             sec_name);
-                       goto err_close_obj;
+                       err = libbpf_prog_type_by_name(sec_name, &prog_type,
+                                                      &expected_attach_type);
+                       if (err < 0) {
+                               p_err("failed to guess program type based on section name %s\n",
+                                     sec_name);
+                               goto err_close_obj;
+                       }
                }
+
+               bpf_program__set_ifindex(pos, ifindex);
+               bpf_program__set_type(pos, prog_type);
+               bpf_program__set_expected_attach_type(pos, expected_attach_type);
        }
-       bpf_program__set_type(prog, attr.prog_type);
-       bpf_program__set_expected_attach_type(prog, expected_attach_type);
 
        qsort(map_replace, old_map_fds, sizeof(*map_replace),
              map_replace_compar);
@@ -998,15 +1071,47 @@ static int do_load(int argc, char **argv)
                goto err_close_obj;
        }
 
+       set_max_rlimit();
+
        err = bpf_object__load(obj);
        if (err) {
                p_err("failed to load object file");
                goto err_close_obj;
        }
 
-       if (do_pin_fd(bpf_program__fd(prog), pinfile))
+       err = mount_bpffs_for_pin(pinfile);
+       if (err)
                goto err_close_obj;
 
+       if (first_prog_only) {
+               prog = bpf_program__next(NULL, obj);
+               if (!prog) {
+                       p_err("object file doesn't contain any bpf program");
+                       goto err_close_obj;
+               }
+
+               err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
+               if (err) {
+                       p_err("failed to pin program %s",
+                             bpf_program__title(prog, false));
+                       goto err_close_obj;
+               }
+       } else {
+               err = bpf_object__pin_programs(obj, pinfile);
+               if (err) {
+                       p_err("failed to pin all programs");
+                       goto err_close_obj;
+               }
+       }
+
+       if (pinmaps) {
+               err = bpf_object__pin_maps(obj, pinmaps);
+               if (err) {
+                       p_err("failed to pin all maps");
+                       goto err_unpin;
+               }
+       }
+
        if (json_output)
                jsonw_null(json_wtr);
 
@@ -1017,6 +1122,11 @@ static int do_load(int argc, char **argv)
 
        return 0;
 
+err_unpin:
+       if (first_prog_only)
+               unlink(pinfile);
+       else
+               bpf_object__unpin_programs(obj, pinfile);
 err_close_obj:
        bpf_object__close(obj);
 err_free_reuse_maps:
@@ -1026,6 +1136,16 @@ err_free_reuse_maps:
        return -1;
 }
 
+static int do_load(int argc, char **argv)
+{
+       return load_with_options(argc, argv, true);
+}
+
+static int do_loadall(int argc, char **argv)
+{
+       return load_with_options(argc, argv, false);
+}
+
 static int do_help(int argc, char **argv)
 {
        if (json_output) {
@@ -1038,10 +1158,12 @@ static int do_help(int argc, char **argv)
                "       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
                "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
                "       %s %s pin   PROG FILE\n"
-               "       %s %s load  OBJ  FILE [type TYPE] [dev NAME] \\\n"
-               "                         [map { idx IDX | name NAME } MAP]\n"
-               "       %s %s attach PROG ATTACH_TYPE MAP\n"
-               "       %s %s detach PROG ATTACH_TYPE MAP\n"
+               "       %s %s { load | loadall } OBJ  PATH \\\n"
+               "                         [type TYPE] [dev NAME] \\\n"
+               "                         [map { idx IDX | name NAME } MAP]\\\n"
+               "                         [pinmaps MAP_DIR]\n"
+               "       %s %s attach PROG ATTACH_TYPE [MAP]\n"
+               "       %s %s detach PROG ATTACH_TYPE [MAP]\n"
                "       %s %s help\n"
                "\n"
                "       " HELP_SPEC_MAP "\n"
@@ -1053,7 +1175,8 @@ static int do_help(int argc, char **argv)
                "                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
                "                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
                "                 cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
-               "       ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse }\n"
+               "       ATTACH_TYPE := { msg_verdict | skb_verdict | skb_parse |\n"
+               "                        flow_dissector }\n"
                "       " HELP_SPEC_OPTIONS "\n"
                "",
                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
@@ -1070,6 +1193,7 @@ static const struct cmd cmds[] = {
        { "dump",       do_dump },
        { "pin",        do_pin },
        { "load",       do_load },
+       { "loadall",    do_loadall },
        { "attach",     do_attach },
        { "detach",     do_detach },
        { 0 }
index 3284759..e06ac02 100644 (file)
@@ -242,11 +242,15 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
                .cb_imm         = print_imm,
                .private_data   = dd,
        };
+       struct bpf_func_info *record;
        struct bpf_insn *insn = buf;
+       struct btf *btf = dd->btf;
        bool double_insn = false;
+       char func_sig[1024];
        unsigned int i;
 
        jsonw_start_array(json_wtr);
+       record = dd->func_info;
        for (i = 0; i < len / sizeof(*insn); i++) {
                if (double_insn) {
                        double_insn = false;
@@ -255,6 +259,20 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 
                jsonw_start_object(json_wtr);
+
+               if (btf && record) {
+                       if (record->insn_offset == i) {
+                               btf_dumper_type_only(btf, record->type_id,
+                                                    func_sig,
+                                                    sizeof(func_sig));
+                               if (func_sig[0] != '\0') {
+                                       jsonw_name(json_wtr, "proto");
+                                       jsonw_string(json_wtr, func_sig);
+                               }
+                               record = (void *)record + dd->finfo_rec_size;
+                       }
+               }
+
                jsonw_name(json_wtr, "disasm");
                print_bpf_insn(&cbs, insn + i, true);
 
@@ -297,16 +315,31 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
                .cb_imm         = print_imm,
                .private_data   = dd,
        };
+       struct bpf_func_info *record;
        struct bpf_insn *insn = buf;
+       struct btf *btf = dd->btf;
        bool double_insn = false;
+       char func_sig[1024];
        unsigned int i;
 
+       record = dd->func_info;
        for (i = 0; i < len / sizeof(*insn); i++) {
                if (double_insn) {
                        double_insn = false;
                        continue;
                }
 
+               if (btf && record) {
+                       if (record->insn_offset == i) {
+                               btf_dumper_type_only(btf, record->type_id,
+                                                    func_sig,
+                                                    sizeof(func_sig));
+                               if (func_sig[0] != '\0')
+                                       printf("%s:\n", func_sig);
+                               record = (void *)record + dd->finfo_rec_size;
+                       }
+               }
+
                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 
                printf("% 4d: ", i);
index 33d86e2..aec3172 100644 (file)
@@ -51,6 +51,9 @@ struct dump_data {
        __u32 sym_count;
        __u64 *jited_ksyms;
        __u32 nr_jited_ksyms;
+       struct btf *btf;
+       void *func_info;
+       __u32 finfo_rec_size;
        char scratch_buff[SYM_MAX_NAME + 8];
 };
 
index 72c453a..ec8b40f 100644 (file)
@@ -257,9 +257,6 @@ enum bpf_attach_type {
 /* Specify numa node during map creation */
 #define BPF_F_NUMA_NODE                (1U << 2)
 
-/* flags for BPF_PROG_QUERY */
-#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
-
 #define BPF_OBJ_NAME_LEN 16U
 
 /* Flags for accessing BPF object */
@@ -269,6 +266,12 @@ enum bpf_attach_type {
 /* Flag for stack_map, store build_id+offset instead of pointer */
 #define BPF_F_STACK_BUILD_ID   (1U << 5)
 
+/* Zero-initialize hash function seed. This should only be used for testing. */
+#define BPF_F_ZERO_SEED                (1U << 6)
+
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -335,6 +338,10 @@ union bpf_attr {
                 * (context accesses, allowed helpers, etc).
                 */
                __u32           expected_attach_type;
+               __u32           prog_btf_fd;    /* fd pointing to BTF type data */
+               __u32           func_info_rec_size;     /* userspace bpf_func_info size */
+               __aligned_u64   func_info;      /* func info */
+               __u32           func_info_cnt;  /* number of bpf_func_info records */
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -2265,6 +2272,19 @@ union bpf_attr {
  *
  *     Return
  *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags)
+ *      Description
+ *             Will remove *pop* bytes from a *msg* starting at byte *start*.
+ *             This may result in **ENOMEM** errors under certain situations if
+ *             an allocation and copy are required due to a full ring buffer.
+ *             However, the helper will try to avoid doing the allocation
+ *             if possible. Other errors can occur if input parameters are
+ *             invalid either due to *start* byte not being valid part of msg
+ *             payload and/or *pop* value being to large.
+ *
+ *     Return
+ *             0 on success, or a negative erro in case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -2357,7 +2377,8 @@ union bpf_attr {
        FN(map_push_elem),              \
        FN(map_pop_elem),               \
        FN(map_peek_elem),              \
-       FN(msg_push_data),
+       FN(msg_push_data),              \
+       FN(msg_pop_data),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2474,6 +2495,7 @@ struct __sk_buff {
 
        __u32 data_meta;
        __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
+       __u64 tstamp;
 };
 
 struct bpf_tunnel_key {
@@ -2649,6 +2671,10 @@ struct bpf_prog_info {
        __u32 nr_jited_func_lens;
        __aligned_u64 jited_ksyms;
        __aligned_u64 jited_func_lens;
+       __u32 btf_id;
+       __u32 func_info_rec_size;
+       __aligned_u64 func_info;
+       __u32 func_info_cnt;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -2960,4 +2986,9 @@ struct bpf_flow_keys {
        };
 };
 
+struct bpf_func_info {
+       __u32   insn_offset;
+       __u32   type_id;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 972265f..14f6694 100644 (file)
@@ -40,7 +40,8 @@ struct btf_type {
        /* "size" is used by INT, ENUM, STRUCT and UNION.
         * "size" tells the size of the type it is describing.
         *
-        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC and FUNC_PROTO.
         * "type" is a type_id referring to another type.
         */
        union {
@@ -64,8 +65,10 @@ struct btf_type {
 #define BTF_KIND_VOLATILE      9       /* Volatile     */
 #define BTF_KIND_CONST         10      /* Const        */
 #define BTF_KIND_RESTRICT      11      /* Restrict     */
-#define BTF_KIND_MAX           11
-#define NR_BTF_KINDS           12
+#define BTF_KIND_FUNC          12      /* Function     */
+#define BTF_KIND_FUNC_PROTO    13      /* Function Proto       */
+#define BTF_KIND_MAX           13
+#define NR_BTF_KINDS           14
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -110,4 +113,13 @@ struct btf_member {
        __u32   offset; /* offset in bits */
 };
 
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
index 425b480..34d9c36 100644 (file)
@@ -66,7 +66,7 @@ ifndef VERBOSE
 endif
 
 FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-mmap bpf reallocarray
+FEATURE_TESTS = libelf libelf-mmap bpf reallocarray cxx
 FEATURE_DISPLAY = libelf bpf
 
 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
@@ -145,14 +145,26 @@ include $(srctree)/tools/build/Makefile.include
 
 BPF_IN    := $(OUTPUT)libbpf-in.o
 LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
+VERSION_SCRIPT := libbpf.map
+
+GLOBAL_SYM_COUNT = $(shell readelf -s $(BPF_IN) | \
+                          awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {s++} END{print s}')
+VERSIONED_SYM_COUNT = $(shell readelf -s $(OUTPUT)libbpf.so | \
+                             grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l)
 
 CMD_TARGETS = $(LIB_FILE)
 
+CXX_TEST_TARGET = $(OUTPUT)test_libbpf
+
+ifeq ($(feature-cxx), 1)
+       CMD_TARGETS += $(CXX_TEST_TARGET)
+endif
+
 TARGETS = $(CMD_TARGETS)
 
 all: fixdep all_cmd
 
-all_cmd: $(CMD_TARGETS)
+all_cmd: $(CMD_TARGETS) check
 
 $(BPF_IN): force elfdep bpfdep
        @(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \
@@ -170,11 +182,27 @@ $(BPF_IN): force elfdep bpfdep
        $(Q)$(MAKE) $(build)=libbpf
 
 $(OUTPUT)libbpf.so: $(BPF_IN)
-       $(QUIET_LINK)$(CC) --shared $^ -o $@
+       $(QUIET_LINK)$(CC) --shared -Wl,--version-script=$(VERSION_SCRIPT) \
+               $^ -o $@
 
 $(OUTPUT)libbpf.a: $(BPF_IN)
        $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
 
+$(OUTPUT)test_libbpf: test_libbpf.cpp $(OUTPUT)libbpf.a
+       $(QUIET_LINK)$(CXX) $^ -lelf -o $@
+
+check: check_abi
+
+check_abi: $(OUTPUT)libbpf.so
+       @if [ "$(GLOBAL_SYM_COUNT)" != "$(VERSIONED_SYM_COUNT)" ]; then  \
+               echo "Warning: Num of global symbols in $(BPF_IN)"       \
+                    "($(GLOBAL_SYM_COUNT)) does NOT match with num of"  \
+                    "versioned symbols in $^ ($(VERSIONED_SYM_COUNT))." \
+                    "Please make sure all LIBBPF_API symbols are"       \
+                    "versioned in $(VERSION_SCRIPT)." >&2;              \
+               exit 1;                                                  \
+       fi
+
 define do_install
        if [ ! -d '$(DESTDIR_SQ)$2' ]; then             \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
@@ -201,8 +229,8 @@ config-clean:
        $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
 
 clean:
-       $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so .*.d .*.cmd \
-               $(RM) LIBBPF-CFLAGS
+       $(call QUIET_CLEAN, libbpf) $(RM) $(TARGETS) $(CXX_TEST_TARGET) \
+               *.o *~ *.a *.so .*.d .*.cmd LIBBPF-CFLAGS
        $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf
 
 
diff --git a/tools/lib/bpf/README.rst b/tools/lib/bpf/README.rst
new file mode 100644 (file)
index 0000000..2ced9e0
--- /dev/null
@@ -0,0 +1,139 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+libbpf API naming convention
+============================
+
+libbpf API provides access to a few logically separated groups of
+functions and types. Every group has its own naming convention
+described here. It's recommended to follow these conventions whenever a
+new function or type is added to keep libbpf API clean and consistent.
+
+All types and functions provided by libbpf API should have one of the
+following prefixes: ``bpf_``, ``btf_``, ``libbpf_``.
+
+System call wrappers
+--------------------
+
+System call wrappers are simple wrappers for commands supported by
+sys_bpf system call. These wrappers should go to ``bpf.h`` header file
+and map one-on-one to corresponding commands.
+
+For example ``bpf_map_lookup_elem`` wraps ``BPF_MAP_LOOKUP_ELEM``
+command of sys_bpf, ``bpf_prog_attach`` wraps ``BPF_PROG_ATTACH``, etc.
+
+Objects
+-------
+
+Another class of types and functions provided by libbpf API is "objects"
+and functions to work with them. Objects are high-level abstractions
+such as BPF program or BPF map. They're represented by corresponding
+structures such as ``struct bpf_object``, ``struct bpf_program``,
+``struct bpf_map``, etc.
+
+Structures are forward declared and access to their fields should be
+provided via corresponding getters and setters rather than directly.
+
+These objects are associated with corresponding parts of ELF object that
+contains compiled BPF programs.
+
+For example ``struct bpf_object`` represents ELF object itself created
+from an ELF file or from a buffer, ``struct bpf_program`` represents a
+program in ELF object and ``struct bpf_map`` is a map.
+
+Functions that work with an object have names built from object name,
+double underscore and part that describes function purpose.
+
+For example ``bpf_object__open`` consists of the name of corresponding
+object, ``bpf_object``, double underscore and ``open`` that defines the
+purpose of the function to open ELF file and create ``bpf_object`` from
+it.
+
+Another example: ``bpf_program__load`` is named for corresponding
+object, ``bpf_program``, that is separated from other part of the name
+by double underscore.
+
+All objects and corresponding functions other than BTF related should go
+to ``libbpf.h``. BTF types and functions should go to ``btf.h``.
+
+Auxiliary functions
+-------------------
+
+Auxiliary functions and types that don't fit well in any of categories
+described above should have ``libbpf_`` prefix, e.g.
+``libbpf_get_error`` or ``libbpf_prog_type_by_name``.
+
+libbpf ABI
+==========
+
+libbpf can be both linked statically or used as DSO. To avoid possible
+conflicts with other libraries an application is linked with, all
+non-static libbpf symbols should have one of the prefixes mentioned in
+API documentation above. See API naming convention to choose the right
+name for a new symbol.
+
+Symbol visibility
+-----------------
+
+libbpf follow the model when all global symbols have visibility "hidden"
+by default and to make a symbol visible it has to be explicitly
+attributed with ``LIBBPF_API`` macro. For example:
+
+.. code-block:: c
+
+        LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
+
+This prevents from accidentally exporting a symbol, that is not supposed
+to be a part of ABI what, in turn, improves both libbpf developer- and
+user-experiences.
+
+ABI versionning
+---------------
+
+To make future ABI extensions possible libbpf ABI is versioned.
+Versioning is implemented by ``libbpf.map`` version script that is
+passed to linker.
+
+Version name is ``LIBBPF_`` prefix + three-component numeric version,
+starting from ``0.0.1``.
+
+Every time ABI is being changed, e.g. because a new symbol is added or
+semantic of existing symbol is changed, ABI version should be bumped.
+
+For example, if current state of ``libbpf.map`` is:
+
+.. code-block::
+        LIBBPF_0.0.1 {
+               global:
+                        bpf_func_a;
+                        bpf_func_b;
+               local:
+                       \*;
+        };
+
+, and a new symbol ``bpf_func_c`` is being introduced, then
+``libbpf.map`` should be changed like this:
+
+.. code-block::
+        LIBBPF_0.0.1 {
+               global:
+                        bpf_func_a;
+                        bpf_func_b;
+               local:
+                       \*;
+        };
+        LIBBPF_0.0.2 {
+                global:
+                        bpf_func_c;
+        } LIBBPF_0.0.1;
+
+, where new version ``LIBBPF_0.0.2`` depends on the previous
+``LIBBPF_0.0.1``.
+
+Format of version script and ways to handle ABI changes, including
+incompatible ones, described in details in [1].
+
+Links
+=====
+
+[1] https://www.akkadia.org/drepper/dsohowto.pdf
+    (Chapter 3. Maintaining APIs and ABIs).
index 03f9bcc..ce18221 100644 (file)
@@ -177,6 +177,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
                           char *log_buf, size_t log_buf_sz)
 {
        union bpf_attr attr;
+       void *finfo = NULL;
        __u32 name_len;
        int fd;
 
@@ -196,6 +197,10 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
        attr.log_level = 0;
        attr.kern_version = load_attr->kern_version;
        attr.prog_ifindex = load_attr->prog_ifindex;
+       attr.prog_btf_fd = load_attr->prog_btf_fd;
+       attr.func_info_rec_size = load_attr->func_info_rec_size;
+       attr.func_info_cnt = load_attr->func_info_cnt;
+       attr.func_info = ptr_to_u64(load_attr->func_info);
        memcpy(attr.prog_name, load_attr->name,
               min(name_len, BPF_OBJ_NAME_LEN - 1));
 
@@ -203,12 +208,55 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
        if (fd >= 0 || !log_buf || !log_buf_sz)
                return fd;
 
+       /* After bpf_prog_load, the kernel may modify certain attributes
+        * to give user space a hint how to deal with loading failure.
+        * Check to see whether we can make some changes and load again.
+        */
+       if (errno == E2BIG && attr.func_info_cnt &&
+           attr.func_info_rec_size < load_attr->func_info_rec_size) {
+               __u32 actual_rec_size = load_attr->func_info_rec_size;
+               __u32 expected_rec_size = attr.func_info_rec_size;
+               __u32 finfo_cnt = load_attr->func_info_cnt;
+               __u64 finfo_len = actual_rec_size * finfo_cnt;
+               const void *orecord;
+               void *nrecord;
+               int i;
+
+               finfo = malloc(finfo_len);
+               if (!finfo)
+                       /* further try with log buffer won't help */
+                       return fd;
+
+               /* zero out bytes kernel does not understand */
+               orecord = load_attr->func_info;
+               nrecord = finfo;
+               for (i = 0; i < load_attr->func_info_cnt; i++) {
+                       memcpy(nrecord, orecord, expected_rec_size);
+                       memset(nrecord + expected_rec_size, 0,
+                              actual_rec_size - expected_rec_size);
+                       orecord += actual_rec_size;
+                       nrecord += actual_rec_size;
+               }
+
+               /* try with corrected func info records */
+               attr.func_info = ptr_to_u64(finfo);
+               attr.func_info_rec_size = load_attr->func_info_rec_size;
+
+               fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+
+               if (fd >= 0 || !log_buf || !log_buf_sz)
+                       goto done;
+       }
+
        /* Try again with log */
        attr.log_buf = ptr_to_u64(log_buf);
        attr.log_size = log_buf_sz;
        attr.log_level = 1;
        log_buf[0] = 0;
-       return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+       fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+done:
+       free(finfo);
+       return fd;
 }
 
 int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
index 26a5153..09e8bbe 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
@@ -74,6 +78,10 @@ struct bpf_load_program_attr {
        const char *license;
        __u32 kern_version;
        __u32 prog_ifindex;
+       __u32 prog_btf_fd;
+       __u32 func_info_rec_size;
+       const void *func_info;
+       __u32 func_info_cnt;
 };
 
 /* Flags to direct loading requirements */
@@ -128,4 +136,9 @@ LIBBPF_API int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf,
 LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
                                 __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
                                 __u64 *probe_offset, __u64 *probe_addr);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* __LIBBPF_BPF_H */
index 449591a..c2d641f 100644 (file)
@@ -37,6 +37,23 @@ struct btf {
        int fd;
 };
 
+struct btf_ext {
+       void *func_info;
+       __u32 func_info_rec_size;
+       __u32 func_info_len;
+};
+
+/* The minimum bpf_func_info checked by the loader */
+struct bpf_func_info_min {
+       __u32   insn_offset;
+       __u32   type_id;
+};
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+       return (__u64) (unsigned long) ptr;
+}
+
 static int btf_add_type(struct btf *btf, struct btf_type *t)
 {
        if (btf->types_size - btf->nr_types < 2) {
@@ -165,6 +182,10 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
                case BTF_KIND_ENUM:
                        next_type += vlen * sizeof(struct btf_enum);
                        break;
+               case BTF_KIND_FUNC_PROTO:
+                       next_type += vlen * sizeof(struct btf_param);
+                       break;
+               case BTF_KIND_FUNC:
                case BTF_KIND_TYPEDEF:
                case BTF_KIND_PTR:
                case BTF_KIND_FWD:
@@ -393,3 +414,329 @@ const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
        else
                return NULL;
 }
+
+int btf__get_from_id(__u32 id, struct btf **btf)
+{
+       struct bpf_btf_info btf_info = { 0 };
+       __u32 len = sizeof(btf_info);
+       __u32 last_size;
+       int btf_fd;
+       void *ptr;
+       int err;
+
+       err = 0;
+       *btf = NULL;
+       btf_fd = bpf_btf_get_fd_by_id(id);
+       if (btf_fd < 0)
+               return 0;
+
+       /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
+        * let's start with a sane default - 4KiB here - and resize it only if
+        * bpf_obj_get_info_by_fd() needs a bigger buffer.
+        */
+       btf_info.btf_size = 4096;
+       last_size = btf_info.btf_size;
+       ptr = malloc(last_size);
+       if (!ptr) {
+               err = -ENOMEM;
+               goto exit_free;
+       }
+
+       bzero(ptr, last_size);
+       btf_info.btf = ptr_to_u64(ptr);
+       err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+
+       if (!err && btf_info.btf_size > last_size) {
+               void *temp_ptr;
+
+               last_size = btf_info.btf_size;
+               temp_ptr = realloc(ptr, last_size);
+               if (!temp_ptr) {
+                       err = -ENOMEM;
+                       goto exit_free;
+               }
+               ptr = temp_ptr;
+               bzero(ptr, last_size);
+               btf_info.btf = ptr_to_u64(ptr);
+               err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+       }
+
+       if (err || btf_info.btf_size > last_size) {
+               err = errno;
+               goto exit_free;
+       }
+
+       *btf = btf__new((__u8 *)(long)btf_info.btf, btf_info.btf_size, NULL);
+       if (IS_ERR(*btf)) {
+               err = PTR_ERR(*btf);
+               *btf = NULL;
+       }
+
+exit_free:
+       close(btf_fd);
+       free(ptr);
+
+       return err;
+}
+
+static int btf_ext_validate_func_info(const void *finfo, __u32 size,
+                                     btf_print_fn_t err_log)
+{
+       int sec_hdrlen = sizeof(struct btf_sec_func_info);
+       __u32 size_left, num_records, record_size;
+       const struct btf_sec_func_info *sinfo;
+       __u64 total_record_size;
+
+       /* At least a func_info record size */
+       if (size < sizeof(__u32)) {
+               elog("BTF.ext func_info record size not found");
+               return -EINVAL;
+       }
+
+       /* The record size needs to meet below minimum standard */
+       record_size = *(__u32 *)finfo;
+       if (record_size < sizeof(struct bpf_func_info_min) ||
+           record_size % sizeof(__u32)) {
+               elog("BTF.ext func_info invalid record size");
+               return -EINVAL;
+       }
+
+       sinfo = finfo + sizeof(__u32);
+       size_left = size - sizeof(__u32);
+
+       /* If no func_info records, return failure now so .BTF.ext
+        * won't be used.
+        */
+       if (!size_left) {
+               elog("BTF.ext no func info records");
+               return -EINVAL;
+       }
+
+       while (size_left) {
+               if (size_left < sec_hdrlen) {
+                       elog("BTF.ext func_info header not found");
+                       return -EINVAL;
+               }
+
+               num_records = sinfo->num_func_info;
+               if (num_records == 0) {
+                       elog("incorrect BTF.ext num_func_info");
+                       return -EINVAL;
+               }
+
+               total_record_size = sec_hdrlen +
+                                   (__u64)num_records * record_size;
+               if (size_left < total_record_size) {
+                       elog("incorrect BTF.ext num_func_info");
+                       return -EINVAL;
+               }
+
+               size_left -= total_record_size;
+               sinfo = (void *)sinfo + total_record_size;
+       }
+
+       return 0;
+}
+
+static int btf_ext_parse_hdr(__u8 *data, __u32 data_size,
+                            btf_print_fn_t err_log)
+{
+       const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
+       __u32 meta_left, last_func_info_pos;
+       void *finfo;
+
+       if (data_size < offsetof(struct btf_ext_header, func_info_off) ||
+           data_size < hdr->hdr_len) {
+               elog("BTF.ext header not found");
+               return -EINVAL;
+       }
+
+       if (hdr->magic != BTF_MAGIC) {
+               elog("Invalid BTF.ext magic:%x\n", hdr->magic);
+               return -EINVAL;
+       }
+
+       if (hdr->version != BTF_VERSION) {
+               elog("Unsupported BTF.ext version:%u\n", hdr->version);
+               return -ENOTSUP;
+       }
+
+       if (hdr->flags) {
+               elog("Unsupported BTF.ext flags:%x\n", hdr->flags);
+               return -ENOTSUP;
+       }
+
+       meta_left = data_size - hdr->hdr_len;
+       if (!meta_left) {
+               elog("BTF.ext has no data\n");
+               return -EINVAL;
+       }
+
+       if (meta_left < hdr->func_info_off) {
+               elog("Invalid BTF.ext func_info section offset:%u\n",
+                    hdr->func_info_off);
+               return -EINVAL;
+       }
+
+       if (hdr->func_info_off & 0x03) {
+               elog("BTF.ext func_info section is not aligned to 4 bytes\n");
+               return -EINVAL;
+       }
+
+       last_func_info_pos = hdr->hdr_len + hdr->func_info_off +
+                            hdr->func_info_len;
+       if (last_func_info_pos > data_size) {
+               elog("Invalid BTF.ext func_info section size:%u\n",
+                    hdr->func_info_len);
+               return -EINVAL;
+       }
+
+       finfo = data + hdr->hdr_len + hdr->func_info_off;
+       return btf_ext_validate_func_info(finfo, hdr->func_info_len,
+                                         err_log);
+}
+
+void btf_ext__free(struct btf_ext *btf_ext)
+{
+       if (!btf_ext)
+               return;
+
+       free(btf_ext->func_info);
+       free(btf_ext);
+}
+
+struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
+{
+       const struct btf_ext_header *hdr;
+       struct btf_ext *btf_ext;
+       void *org_fdata, *fdata;
+       __u32 hdrlen, size_u32;
+       int err;
+
+       err = btf_ext_parse_hdr(data, size, err_log);
+       if (err)
+               return ERR_PTR(err);
+
+       btf_ext = calloc(1, sizeof(struct btf_ext));
+       if (!btf_ext)
+               return ERR_PTR(-ENOMEM);
+
+       hdr = (const struct btf_ext_header *)data;
+       hdrlen = hdr->hdr_len;
+       size_u32 = sizeof(__u32);
+       fdata = malloc(hdr->func_info_len - size_u32);
+       if (!fdata) {
+               free(btf_ext);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       /* remember record size and copy rest of func_info data */
+       org_fdata = data + hdrlen + hdr->func_info_off;
+       btf_ext->func_info_rec_size = *(__u32 *)org_fdata;
+       memcpy(fdata, org_fdata + size_u32, hdr->func_info_len - size_u32);
+       btf_ext->func_info = fdata;
+       btf_ext->func_info_len = hdr->func_info_len - size_u32;
+
+       return btf_ext;
+}
+
+int btf_ext__reloc_init(struct btf *btf, struct btf_ext *btf_ext,
+                       const char *sec_name, void **func_info,
+                       __u32 *func_info_rec_size, __u32 *func_info_len)
+{
+       __u32 sec_hdrlen = sizeof(struct btf_sec_func_info);
+       __u32 i, record_size, records_len;
+       struct btf_sec_func_info *sinfo;
+       const char *info_sec_name;
+       __s64 remain_len;
+       void *data;
+
+       record_size = btf_ext->func_info_rec_size;
+       sinfo = btf_ext->func_info;
+       remain_len = btf_ext->func_info_len;
+
+       while (remain_len > 0) {
+               records_len = sinfo->num_func_info * record_size;
+               info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
+               if (strcmp(info_sec_name, sec_name)) {
+                       remain_len -= sec_hdrlen + records_len;
+                       sinfo = (void *)sinfo + sec_hdrlen + records_len;
+                       continue;
+               }
+
+               data = malloc(records_len);
+               if (!data)
+                       return -ENOMEM;
+
+               memcpy(data, sinfo->data, records_len);
+
+               /* adjust the insn_offset, the data in .BTF.ext is
+                * the actual byte offset, and the kernel expects
+                * the offset in term of bpf_insn.
+                *
+                * adjust the insn offset only, the rest data will
+                * be passed to kernel.
+                */
+               for (i = 0; i < sinfo->num_func_info; i++) {
+                       struct bpf_func_info_min *record;
+
+                       record = data + i * record_size;
+                       record->insn_offset /= sizeof(struct bpf_insn);
+               }
+
+               *func_info = data;
+               *func_info_len = records_len;
+               *func_info_rec_size = record_size;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int btf_ext__reloc(struct btf *btf, struct btf_ext *btf_ext,
+                  const char *sec_name, __u32 insns_cnt,
+                  void **func_info, __u32 *func_info_len)
+{
+       __u32 sec_hdrlen = sizeof(struct btf_sec_func_info);
+       __u32 i, record_size, existing_flen, records_len;
+       struct btf_sec_func_info *sinfo;
+       const char *info_sec_name;
+       __u64 remain_len;
+       void *data;
+
+       record_size = btf_ext->func_info_rec_size;
+       sinfo = btf_ext->func_info;
+       remain_len = btf_ext->func_info_len;
+       while (remain_len > 0) {
+               records_len = sinfo->num_func_info * record_size;
+               info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
+               if (strcmp(info_sec_name, sec_name)) {
+                       remain_len -= sec_hdrlen + records_len;
+                       sinfo = (void *)sinfo + sec_hdrlen + records_len;
+                       continue;
+               }
+
+               existing_flen = *func_info_len;
+               data = realloc(*func_info, existing_flen + records_len);
+               if (!data)
+                       return -ENOMEM;
+
+               memcpy(data + existing_flen, sinfo->data, records_len);
+               /* adjust insn_offset only, the rest data will be passed
+                * to the kernel.
+                */
+               for (i = 0; i < sinfo->num_func_info; i++) {
+                       struct bpf_func_info_min *record;
+
+                       record = data + existing_flen + i * record_size;
+                       record->insn_offset =
+                               record->insn_offset / sizeof(struct bpf_insn) +
+                               insns_cnt;
+               }
+               *func_info = data;
+               *func_info_len = existing_flen + records_len;
+               return 0;
+       }
+
+       return -EINVAL;
+}
index b77e708..5336b2f 100644 (file)
@@ -6,15 +6,60 @@
 
 #include <linux/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
 
 #define BTF_ELF_SEC ".BTF"
+#define BTF_EXT_ELF_SEC ".BTF.ext"
 
 struct btf;
+struct btf_ext;
 struct btf_type;
 
+/*
+ * The .BTF.ext ELF section layout defined as
+ *   struct btf_ext_header
+ *   func_info subsection
+ *
+ * The func_info subsection layout:
+ *   record size for struct bpf_func_info in the func_info subsection
+ *   struct btf_sec_func_info for section #1
+ *   a list of bpf_func_info records for section #1
+ *     where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
+ *     but may not be identical
+ *   struct btf_sec_func_info for section #2
+ *   a list of bpf_func_info records for section #2
+ *   ......
+ *
+ * Note that the bpf_func_info record size in .BTF.ext may not
+ * be the same as the one defined in include/uapi/linux/bpf.h.
+ * The loader should ensure that record_size meets minimum
+ * requirement and pass the record as is to the kernel. The
+ * kernel will handle the func_info properly based on its contents.
+ */
+struct btf_ext_header {
+       __u16   magic;
+       __u8    version;
+       __u8    flags;
+       __u32   hdr_len;
+
+       /* All offsets are in bytes relative to the end of this header */
+       __u32   func_info_off;
+       __u32   func_info_len;
+};
+
+struct btf_sec_func_info {
+       __u32   sec_name_off;
+       __u32   num_func_info;
+       /* Followed by num_func_info number of bpf func_info records */
+       __u8    data[0];
+};
+
 typedef int (*btf_print_fn_t)(const char *, ...)
        __attribute__((format(printf, 1, 2)));
 
@@ -28,5 +73,19 @@ LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
 LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
 LIBBPF_API int btf__fd(const struct btf *btf);
 LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
+LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
+
+struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
+void btf_ext__free(struct btf_ext *btf_ext);
+int btf_ext__reloc_init(struct btf *btf, struct btf_ext *btf_ext,
+                       const char *sec_name, void **func_info,
+                       __u32 *func_info_rec_size, __u32 *func_info_len);
+int btf_ext__reloc(struct btf *btf, struct btf_ext *btf_ext,
+                  const char *sec_name, __u32 insns_cnt, void **func_info,
+                  __u32 *func_info_len);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
 
 #endif /* __LIBBPF_BTF_H */
index d6e62e9..59b748e 100644 (file)
@@ -9,7 +9,9 @@
  * Copyright (C) 2017 Nicira, Inc.
  */
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -24,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/bpf.h>
 #include <linux/btf.h>
+#include <linux/filter.h>
 #include <linux/list.h>
 #include <linux/limits.h>
 #include <linux/perf_event.h>
@@ -114,6 +117,11 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
 #endif
 
+struct bpf_capabilities {
+       /* v4.14: kernel support for program & map names. */
+       __u32 name:1;
+};
+
 /*
  * bpf_prog should be a better name but it has been used in
  * linux/filter.h.
@@ -124,6 +132,10 @@ struct bpf_program {
        char *name;
        int prog_ifindex;
        char *section_name;
+       /* section_name with / replaced by _; makes recursive pinning
+        * in bpf_object__pin_programs easier
+        */
+       char *pin_name;
        struct bpf_insn *insns;
        size_t insns_cnt, main_prog_cnt;
        enum bpf_prog_type type;
@@ -152,6 +164,12 @@ struct bpf_program {
        bpf_program_clear_priv_t clear_priv;
 
        enum bpf_attach_type expected_attach_type;
+       int btf_fd;
+       void *func_info;
+       __u32 func_info_rec_size;
+       __u32 func_info_len;
+
+       struct bpf_capabilities *caps;
 };
 
 struct bpf_map {
@@ -159,6 +177,7 @@ struct bpf_map {
        char *name;
        size_t offset;
        int map_ifindex;
+       int inner_map_fd;
        struct bpf_map_def def;
        __u32 btf_key_type_id;
        __u32 btf_value_type_id;
@@ -208,10 +227,13 @@ struct bpf_object {
        struct list_head list;
 
        struct btf *btf;
+       struct btf_ext *btf_ext;
 
        void *priv;
        bpf_object_clear_priv_t clear_priv;
 
+       struct bpf_capabilities caps;
+
        char path[];
 };
 #define obj_elf_valid(o)       ((o)->efile.elf)
@@ -237,6 +259,9 @@ void bpf_program__unload(struct bpf_program *prog)
 
        prog->instances.nr = -1;
        zfree(&prog->instances.fds);
+
+       zclose(prog->btf_fd);
+       zfree(&prog->func_info);
 }
 
 static void bpf_program__exit(struct bpf_program *prog)
@@ -253,6 +278,7 @@ static void bpf_program__exit(struct bpf_program *prog)
        bpf_program__unload(prog);
        zfree(&prog->name);
        zfree(&prog->section_name);
+       zfree(&prog->pin_name);
        zfree(&prog->insns);
        zfree(&prog->reloc_desc);
 
@@ -261,6 +287,17 @@ static void bpf_program__exit(struct bpf_program *prog)
        prog->idx = -1;
 }
 
+static char *__bpf_program__pin_name(struct bpf_program *prog)
+{
+       char *name, *p;
+
+       name = p = strdup(prog->section_name);
+       while ((p = strchr(p, '/')))
+               *p = '_';
+
+       return name;
+}
+
 static int
 bpf_program__init(void *data, size_t size, char *section_name, int idx,
                  struct bpf_program *prog)
@@ -279,6 +316,13 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
                goto errout;
        }
 
+       prog->pin_name = __bpf_program__pin_name(prog);
+       if (!prog->pin_name) {
+               pr_warning("failed to alloc pin name for prog under section(%d) %s\n",
+                          idx, section_name);
+               goto errout;
+       }
+
        prog->insns = malloc(size);
        if (!prog->insns) {
                pr_warning("failed to alloc insns for prog under section %s\n",
@@ -291,7 +335,8 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
        prog->idx = idx;
        prog->instances.fds = NULL;
        prog->instances.nr = -1;
-       prog->type = BPF_PROG_TYPE_KPROBE;
+       prog->type = BPF_PROG_TYPE_UNSPEC;
+       prog->btf_fd = -1;
 
        return 0;
 errout:
@@ -310,6 +355,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
        if (err)
                return err;
 
+       prog.caps = &obj->caps;
        progs = obj->programs;
        nr_progs = obj->nr_programs;
 
@@ -562,6 +608,14 @@ static int compare_bpf_map(const void *_a, const void *_b)
        return a->offset - b->offset;
 }
 
+static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
+{
+       if (type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+           type == BPF_MAP_TYPE_HASH_OF_MAPS)
+               return true;
+       return false;
+}
+
 static int
 bpf_object__init_maps(struct bpf_object *obj, int flags)
 {
@@ -625,13 +679,15 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
        }
        obj->nr_maps = nr_maps;
 
-       /*
-        * fill all fd with -1 so won't close incorrect
-        * fd (fd=0 is stdin) when failure (zclose won't close
-        * negative fd)).
-        */
-       for (i = 0; i < nr_maps; i++)
+       for (i = 0; i < nr_maps; i++) {
+               /*
+                * fill all fd with -1 so won't close incorrect
+                * fd (fd=0 is stdin) when failure (zclose won't close
+                * negative fd)).
+                */
                obj->maps[i].fd = -1;
+               obj->maps[i].inner_map_fd = -1;
+       }
 
        /*
         * Fill obj->maps using data in "maps" section.
@@ -784,6 +840,15 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
                                           BTF_ELF_SEC, PTR_ERR(obj->btf));
                                obj->btf = NULL;
                        }
+               } else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+                       obj->btf_ext = btf_ext__new(data->d_buf, data->d_size,
+                                                   __pr_debug);
+                       if (IS_ERR(obj->btf_ext)) {
+                               pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
+                                          BTF_EXT_ELF_SEC,
+                                          PTR_ERR(obj->btf_ext));
+                               obj->btf_ext = NULL;
+                       }
                } else if (sh.sh_type == SHT_SYMTAB) {
                        if (obj->efile.symbols) {
                                pr_warning("bpf: multiple SYMTAB in %s\n",
@@ -1095,6 +1160,52 @@ err_free_new_name:
 }
 
 static int
+bpf_object__probe_name(struct bpf_object *obj)
+{
+       struct bpf_load_program_attr attr;
+       char *cp, errmsg[STRERR_BUFSIZE];
+       struct bpf_insn insns[] = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       };
+       int ret;
+
+       /* make sure basic loading works */
+
+       memset(&attr, 0, sizeof(attr));
+       attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+       attr.insns = insns;
+       attr.insns_cnt = ARRAY_SIZE(insns);
+       attr.license = "GPL";
+
+       ret = bpf_load_program_xattr(&attr, NULL, 0);
+       if (ret < 0) {
+               cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+               pr_warning("Error in %s():%s(%d). Couldn't load basic 'r0 = 0' BPF program.\n",
+                          __func__, cp, errno);
+               return -errno;
+       }
+       close(ret);
+
+       /* now try the same program, but with the name */
+
+       attr.name = "test";
+       ret = bpf_load_program_xattr(&attr, NULL, 0);
+       if (ret >= 0) {
+               obj->caps.name = 1;
+               close(ret);
+       }
+
+       return 0;
+}
+
+static int
+bpf_object__probe_caps(struct bpf_object *obj)
+{
+       return bpf_object__probe_name(obj);
+}
+
+static int
 bpf_object__create_maps(struct bpf_object *obj)
 {
        struct bpf_create_map_attr create_attr = {};
@@ -1113,7 +1224,8 @@ bpf_object__create_maps(struct bpf_object *obj)
                        continue;
                }
 
-               create_attr.name = map->name;
+               if (obj->caps.name)
+                       create_attr.name = map->name;
                create_attr.map_ifindex = map->map_ifindex;
                create_attr.map_type = def->type;
                create_attr.map_flags = def->map_flags;
@@ -1123,6 +1235,9 @@ bpf_object__create_maps(struct bpf_object *obj)
                create_attr.btf_fd = 0;
                create_attr.btf_key_type_id = 0;
                create_attr.btf_value_type_id = 0;
+               if (bpf_map_type__is_map_in_map(def->type) &&
+                   map->inner_map_fd >= 0)
+                       create_attr.inner_map_fd = map->inner_map_fd;
 
                if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
                        create_attr.btf_fd = btf__fd(obj->btf);
@@ -1167,6 +1282,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
        struct bpf_insn *insn, *new_insn;
        struct bpf_program *text;
        size_t new_cnt;
+       int err;
 
        if (relo->type != RELO_CALL)
                return -LIBBPF_ERRNO__RELOC;
@@ -1189,6 +1305,20 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
                        pr_warning("oom in prog realloc\n");
                        return -ENOMEM;
                }
+
+               if (obj->btf && obj->btf_ext) {
+                       err = btf_ext__reloc(obj->btf, obj->btf_ext,
+                                            text->section_name,
+                                            prog->insns_cnt,
+                                            &prog->func_info,
+                                            &prog->func_info_len);
+                       if (err) {
+                               pr_warning("error in btf_ext__reloc for sec %s\n",
+                                          text->section_name);
+                               return err;
+                       }
+               }
+
                memcpy(new_insn + prog->insns_cnt, text->insns,
                       text->insns_cnt * sizeof(*insn));
                prog->insns = new_insn;
@@ -1208,7 +1338,24 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
 {
        int i, err;
 
-       if (!prog || !prog->reloc_desc)
+       if (!prog)
+               return 0;
+
+       if (obj->btf && obj->btf_ext) {
+               err = btf_ext__reloc_init(obj->btf, obj->btf_ext,
+                                         prog->section_name,
+                                         &prog->func_info,
+                                         &prog->func_info_rec_size,
+                                         &prog->func_info_len);
+               if (err) {
+                       pr_warning("err in btf_ext__reloc_init for sec %s\n",
+                                  prog->section_name);
+                       return err;
+               }
+               prog->btf_fd = btf__fd(obj->btf);
+       }
+
+       if (!prog->reloc_desc)
                return 0;
 
        for (i = 0; i < prog->nr_reloc; i++) {
@@ -1296,9 +1443,9 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 }
 
 static int
-load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
-            const char *name, struct bpf_insn *insns, int insns_cnt,
-            char *license, __u32 kern_version, int *pfd, int prog_ifindex)
+load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
+            char *license, __u32 kern_version, int *pfd,
+            __u32 func_info_cnt)
 {
        struct bpf_load_program_attr load_attr;
        char *cp, errmsg[STRERR_BUFSIZE];
@@ -1306,14 +1453,19 @@ load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
        int ret;
 
        memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
-       load_attr.prog_type = type;
-       load_attr.expected_attach_type = expected_attach_type;
-       load_attr.name = name;
+       load_attr.prog_type = prog->type;
+       load_attr.expected_attach_type = prog->expected_attach_type;
+       if (prog->caps->name)
+               load_attr.name = prog->name;
        load_attr.insns = insns;
        load_attr.insns_cnt = insns_cnt;
        load_attr.license = license;
        load_attr.kern_version = kern_version;
-       load_attr.prog_ifindex = prog_ifindex;
+       load_attr.prog_ifindex = prog->prog_ifindex;
+       load_attr.prog_btf_fd = prog->btf_fd >= 0 ? prog->btf_fd : 0;
+       load_attr.func_info = prog->func_info;
+       load_attr.func_info_rec_size = prog->func_info_rec_size;
+       load_attr.func_info_cnt = func_info_cnt;
 
        if (!load_attr.insns || !load_attr.insns_cnt)
                return -EINVAL;
@@ -1371,8 +1523,14 @@ int
 bpf_program__load(struct bpf_program *prog,
                  char *license, __u32 kern_version)
 {
+       __u32 func_info_cnt;
        int err = 0, fd, i;
 
+       if (prog->func_info_len == 0)
+               func_info_cnt = 0;
+       else
+               func_info_cnt = prog->func_info_len / prog->func_info_rec_size;
+
        if (prog->instances.nr < 0 || !prog->instances.fds) {
                if (prog->preprocessor) {
                        pr_warning("Internal error: can't load program '%s'\n",
@@ -1394,10 +1552,9 @@ bpf_program__load(struct bpf_program *prog,
                        pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
                                   prog->section_name, prog->instances.nr);
                }
-               err = load_program(prog->type, prog->expected_attach_type,
-                                  prog->name, prog->insns, prog->insns_cnt,
+               err = load_program(prog, prog->insns, prog->insns_cnt,
                                   license, kern_version, &fd,
-                                  prog->prog_ifindex);
+                                  func_info_cnt);
                if (!err)
                        prog->instances.fds[0] = fd;
                goto out;
@@ -1425,11 +1582,10 @@ bpf_program__load(struct bpf_program *prog,
                        continue;
                }
 
-               err = load_program(prog->type, prog->expected_attach_type,
-                                  prog->name, result.new_insn_ptr,
+               err = load_program(prog, result.new_insn_ptr,
                                   result.new_insn_cnt,
                                   license, kern_version, &fd,
-                                  prog->prog_ifindex);
+                                  func_info_cnt);
 
                if (err) {
                        pr_warning("Loading the %dth instance of program '%s' failed\n",
@@ -1495,12 +1651,12 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
        case BPF_PROG_TYPE_LIRC_MODE2:
        case BPF_PROG_TYPE_SK_REUSEPORT:
        case BPF_PROG_TYPE_FLOW_DISSECTOR:
-               return false;
        case BPF_PROG_TYPE_UNSPEC:
-       case BPF_PROG_TYPE_KPROBE:
        case BPF_PROG_TYPE_TRACEPOINT:
-       case BPF_PROG_TYPE_PERF_EVENT:
        case BPF_PROG_TYPE_RAW_TRACEPOINT:
+       case BPF_PROG_TYPE_PERF_EVENT:
+               return false;
+       case BPF_PROG_TYPE_KPROBE:
        default:
                return true;
        }
@@ -1627,6 +1783,7 @@ int bpf_object__load(struct bpf_object *obj)
 
        obj->loaded = true;
 
+       CHECK_ERR(bpf_object__probe_caps(obj), err, out);
        CHECK_ERR(bpf_object__create_maps(obj), err, out);
        CHECK_ERR(bpf_object__relocate(obj), err, out);
        CHECK_ERR(bpf_object__load_progs(obj), err, out);
@@ -1699,6 +1856,34 @@ int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
        return 0;
 }
 
+int bpf_program__unpin_instance(struct bpf_program *prog, const char *path,
+                               int instance)
+{
+       int err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (prog == NULL) {
+               pr_warning("invalid program pointer\n");
+               return -EINVAL;
+       }
+
+       if (instance < 0 || instance >= prog->instances.nr) {
+               pr_warning("invalid prog instance %d of prog %s (max %d)\n",
+                          instance, prog->section_name, prog->instances.nr);
+               return -EINVAL;
+       }
+
+       err = unlink(path);
+       if (err != 0)
+               return -errno;
+       pr_debug("unpinned program '%s'\n", path);
+
+       return 0;
+}
+
 static int make_dir(const char *path)
 {
        char *cp, errmsg[STRERR_BUFSIZE];
@@ -1733,6 +1918,11 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
                return -EINVAL;
        }
 
+       if (prog->instances.nr == 1) {
+               /* don't create subdirs when pinning single instance */
+               return bpf_program__pin_instance(prog, path, 0);
+       }
+
        err = make_dir(path);
        if (err)
                return err;
@@ -1742,16 +1932,83 @@ int bpf_program__pin(struct bpf_program *prog, const char *path)
                int len;
 
                len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin;
+               }
+
+               err = bpf_program__pin_instance(prog, buf, i);
+               if (err)
+                       goto err_unpin;
+       }
+
+       return 0;
+
+err_unpin:
+       for (i = i - 1; i >= 0; i--) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_program__unpin_instance(prog, buf, i);
+       }
+
+       rmdir(path);
+
+       return err;
+}
+
+int bpf_program__unpin(struct bpf_program *prog, const char *path)
+{
+       int i, err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (prog == NULL) {
+               pr_warning("invalid program pointer\n");
+               return -EINVAL;
+       }
+
+       if (prog->instances.nr <= 0) {
+               pr_warning("no instances of prog %s to pin\n",
+                          prog->section_name);
+               return -EINVAL;
+       }
+
+       if (prog->instances.nr == 1) {
+               /* don't create subdirs when pinning single instance */
+               return bpf_program__unpin_instance(prog, path, 0);
+       }
+
+       for (i = 0; i < prog->instances.nr; i++) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
                if (len < 0)
                        return -EINVAL;
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_program__pin_instance(prog, buf, i);
+               err = bpf_program__unpin_instance(prog, buf, i);
                if (err)
                        return err;
        }
 
+       err = rmdir(path);
+       if (err)
+               return -errno;
+
        return 0;
 }
 
@@ -1776,12 +2033,33 @@ int bpf_map__pin(struct bpf_map *map, const char *path)
        }
 
        pr_debug("pinned map '%s'\n", path);
+
        return 0;
 }
 
-int bpf_object__pin(struct bpf_object *obj, const char *path)
+int bpf_map__unpin(struct bpf_map *map, const char *path)
+{
+       int err;
+
+       err = check_path(path);
+       if (err)
+               return err;
+
+       if (map == NULL) {
+               pr_warning("invalid map pointer\n");
+               return -EINVAL;
+       }
+
+       err = unlink(path);
+       if (err != 0)
+               return -errno;
+       pr_debug("unpinned map '%s'\n", path);
+
+       return 0;
+}
+
+int bpf_object__pin_maps(struct bpf_object *obj, const char *path)
 {
-       struct bpf_program *prog;
        struct bpf_map *map;
        int err;
 
@@ -1803,28 +2081,142 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
 
                len = snprintf(buf, PATH_MAX, "%s/%s", path,
                               bpf_map__name(map));
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin_maps;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin_maps;
+               }
+
+               err = bpf_map__pin(map, buf);
+               if (err)
+                       goto err_unpin_maps;
+       }
+
+       return 0;
+
+err_unpin_maps:
+       while ((map = bpf_map__prev(map, obj))) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              bpf_map__name(map));
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_map__unpin(map, buf);
+       }
+
+       return err;
+}
+
+int bpf_object__unpin_maps(struct bpf_object *obj, const char *path)
+{
+       struct bpf_map *map;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
+       bpf_map__for_each(map, obj) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              bpf_map__name(map));
                if (len < 0)
                        return -EINVAL;
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_map__pin(map, buf);
+               err = bpf_map__unpin(map, buf);
                if (err)
                        return err;
        }
 
+       return 0;
+}
+
+int bpf_object__pin_programs(struct bpf_object *obj, const char *path)
+{
+       struct bpf_program *prog;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
+       if (!obj->loaded) {
+               pr_warning("object not yet loaded; load it first\n");
+               return -ENOENT;
+       }
+
+       err = make_dir(path);
+       if (err)
+               return err;
+
+       bpf_object__for_each_program(prog, obj) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              prog->pin_name);
+               if (len < 0) {
+                       err = -EINVAL;
+                       goto err_unpin_programs;
+               } else if (len >= PATH_MAX) {
+                       err = -ENAMETOOLONG;
+                       goto err_unpin_programs;
+               }
+
+               err = bpf_program__pin(prog, buf);
+               if (err)
+                       goto err_unpin_programs;
+       }
+
+       return 0;
+
+err_unpin_programs:
+       while ((prog = bpf_program__prev(prog, obj))) {
+               char buf[PATH_MAX];
+               int len;
+
+               len = snprintf(buf, PATH_MAX, "%s/%s", path,
+                              prog->pin_name);
+               if (len < 0)
+                       continue;
+               else if (len >= PATH_MAX)
+                       continue;
+
+               bpf_program__unpin(prog, buf);
+       }
+
+       return err;
+}
+
+int bpf_object__unpin_programs(struct bpf_object *obj, const char *path)
+{
+       struct bpf_program *prog;
+       int err;
+
+       if (!obj)
+               return -ENOENT;
+
        bpf_object__for_each_program(prog, obj) {
                char buf[PATH_MAX];
                int len;
 
                len = snprintf(buf, PATH_MAX, "%s/%s", path,
-                              prog->section_name);
+                              prog->pin_name);
                if (len < 0)
                        return -EINVAL;
                else if (len >= PATH_MAX)
                        return -ENAMETOOLONG;
 
-               err = bpf_program__pin(prog, buf);
+               err = bpf_program__unpin(prog, buf);
                if (err)
                        return err;
        }
@@ -1832,6 +2224,23 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
        return 0;
 }
 
+int bpf_object__pin(struct bpf_object *obj, const char *path)
+{
+       int err;
+
+       err = bpf_object__pin_maps(obj, path);
+       if (err)
+               return err;
+
+       err = bpf_object__pin_programs(obj, path);
+       if (err) {
+               bpf_object__unpin_maps(obj, path);
+               return err;
+       }
+
+       return 0;
+}
+
 void bpf_object__close(struct bpf_object *obj)
 {
        size_t i;
@@ -1845,6 +2254,7 @@ void bpf_object__close(struct bpf_object *obj)
        bpf_object__elf_finish(obj);
        bpf_object__unload(obj);
        btf__free(obj->btf);
+       btf_ext__free(obj->btf_ext);
 
        for (i = 0; i < obj->nr_maps; i++) {
                zfree(&obj->maps[i].name);
@@ -1918,23 +2328,26 @@ void *bpf_object__priv(struct bpf_object *obj)
 }
 
 static struct bpf_program *
-__bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+__bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
 {
-       size_t idx;
+       size_t nr_programs = obj->nr_programs;
+       ssize_t idx;
 
-       if (!obj->programs)
+       if (!nr_programs)
                return NULL;
-       /* First handler */
-       if (prev == NULL)
-               return &obj->programs[0];
 
-       if (prev->obj != obj) {
+       if (!p)
+               /* Iter from the beginning */
+               return forward ? &obj->programs[0] :
+                       &obj->programs[nr_programs - 1];
+
+       if (p->obj != obj) {
                pr_warning("error: program handler doesn't match object\n");
                return NULL;
        }
 
-       idx = (prev - obj->programs) + 1;
-       if (idx >= obj->nr_programs)
+       idx = (p - obj->programs) + (forward ? 1 : -1);
+       if (idx >= obj->nr_programs || idx < 0)
                return NULL;
        return &obj->programs[idx];
 }
@@ -1945,7 +2358,19 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
        struct bpf_program *prog = prev;
 
        do {
-               prog = __bpf_program__next(prog, obj);
+               prog = __bpf_program__iter(prog, obj, true);
+       } while (prog && bpf_program__is_function_storage(prog, obj));
+
+       return prog;
+}
+
+struct bpf_program *
+bpf_program__prev(struct bpf_program *next, struct bpf_object *obj)
+{
+       struct bpf_program *prog = next;
+
+       do {
+               prog = __bpf_program__iter(prog, obj, false);
        } while (prog && bpf_program__is_function_storage(prog, obj));
 
        return prog;
@@ -2272,10 +2697,24 @@ void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
        map->map_ifindex = ifindex;
 }
 
-struct bpf_map *
-bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
 {
-       size_t idx;
+       if (!bpf_map_type__is_map_in_map(map->def.type)) {
+               pr_warning("error: unsupported map type\n");
+               return -EINVAL;
+       }
+       if (map->inner_map_fd != -1) {
+               pr_warning("error: inner_map_fd already specified\n");
+               return -EINVAL;
+       }
+       map->inner_map_fd = fd;
+       return 0;
+}
+
+static struct bpf_map *
+__bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
+{
+       ssize_t idx;
        struct bpf_map *s, *e;
 
        if (!obj || !obj->maps)
@@ -2284,22 +2723,40 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
        s = obj->maps;
        e = obj->maps + obj->nr_maps;
 
-       if (prev == NULL)
-               return s;
-
-       if ((prev < s) || (prev >= e)) {
+       if ((m < s) || (m >= e)) {
                pr_warning("error in %s: map handler doesn't belong to object\n",
                           __func__);
                return NULL;
        }
 
-       idx = (prev - obj->maps) + 1;
-       if (idx >= obj->nr_maps)
+       idx = (m - obj->maps) + i;
+       if (idx >= obj->nr_maps || idx < 0)
                return NULL;
        return &obj->maps[idx];
 }
 
 struct bpf_map *
+bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+{
+       if (prev == NULL)
+               return obj->maps;
+
+       return __bpf_map__iter(prev, obj, 1);
+}
+
+struct bpf_map *
+bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
+{
+       if (next == NULL) {
+               if (!obj->nr_maps)
+                       return NULL;
+               return obj->maps + obj->nr_maps - 1;
+       }
+
+       return __bpf_map__iter(next, obj, -1);
+}
+
+struct bpf_map *
 bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
 {
        struct bpf_map *pos;
index 1f3468d..f30c3d0 100644 (file)
 #include <sys/types.h>  // for size_t
 #include <linux/bpf.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifndef LIBBPF_API
 #define LIBBPF_API __attribute__((visibility("default")))
 #endif
@@ -71,6 +75,13 @@ struct bpf_object *__bpf_object__open_xattr(struct bpf_object_open_attr *attr,
 LIBBPF_API struct bpf_object *bpf_object__open_buffer(void *obj_buf,
                                                      size_t obj_buf_sz,
                                                      const char *name);
+LIBBPF_API int bpf_object__pin_maps(struct bpf_object *obj, const char *path);
+LIBBPF_API int bpf_object__unpin_maps(struct bpf_object *obj,
+                                     const char *path);
+LIBBPF_API int bpf_object__pin_programs(struct bpf_object *obj,
+                                       const char *path);
+LIBBPF_API int bpf_object__unpin_programs(struct bpf_object *obj,
+                                         const char *path);
 LIBBPF_API int bpf_object__pin(struct bpf_object *object, const char *path);
 LIBBPF_API void bpf_object__close(struct bpf_object *object);
 
@@ -112,6 +123,9 @@ LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
             (pos) != NULL;                             \
             (pos) = bpf_program__next((pos), (obj)))
 
+LIBBPF_API struct bpf_program *bpf_program__prev(struct bpf_program *prog,
+                                                struct bpf_object *obj);
+
 typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
                                         void *);
 
@@ -131,7 +145,11 @@ LIBBPF_API int bpf_program__fd(struct bpf_program *prog);
 LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
                                         const char *path,
                                         int instance);
+LIBBPF_API int bpf_program__unpin_instance(struct bpf_program *prog,
+                                          const char *path,
+                                          int instance);
 LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
+LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
 LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
 
 struct bpf_insn;
@@ -260,6 +278,9 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
             (pos) != NULL;                             \
             (pos) = bpf_map__next((pos), (obj)))
 
+LIBBPF_API struct bpf_map *
+bpf_map__prev(struct bpf_map *map, struct bpf_object *obj);
+
 LIBBPF_API int bpf_map__fd(struct bpf_map *map);
 LIBBPF_API const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
 LIBBPF_API const char *bpf_map__name(struct bpf_map *map);
@@ -274,6 +295,9 @@ LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
 LIBBPF_API bool bpf_map__is_offload_neutral(struct bpf_map *map);
 LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
 LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
+LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
+
+LIBBPF_API int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd);
 
 LIBBPF_API long libbpf_get_error(const void *ptr);
 
@@ -317,4 +341,9 @@ int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
                        libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
 int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
                         libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* __LIBBPF_LIBBPF_H */
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
new file mode 100644 (file)
index 0000000..4fb29f6
--- /dev/null
@@ -0,0 +1,121 @@
+LIBBPF_0.0.1 {
+       global:
+               bpf_btf_get_fd_by_id;
+               bpf_create_map;
+               bpf_create_map_in_map;
+               bpf_create_map_in_map_node;
+               bpf_create_map_name;
+               bpf_create_map_node;
+               bpf_create_map_xattr;
+               bpf_load_btf;
+               bpf_load_program;
+               bpf_load_program_xattr;
+               bpf_map__btf_key_type_id;
+               bpf_map__btf_value_type_id;
+               bpf_map__def;
+               bpf_map__fd;
+               bpf_map__is_offload_neutral;
+               bpf_map__name;
+               bpf_map__next;
+               bpf_map__pin;
+               bpf_map__prev;
+               bpf_map__priv;
+               bpf_map__reuse_fd;
+               bpf_map__set_ifindex;
+               bpf_map__set_inner_map_fd;
+               bpf_map__set_priv;
+               bpf_map__unpin;
+               bpf_map_delete_elem;
+               bpf_map_get_fd_by_id;
+               bpf_map_get_next_id;
+               bpf_map_get_next_key;
+               bpf_map_lookup_and_delete_elem;
+               bpf_map_lookup_elem;
+               bpf_map_update_elem;
+               bpf_obj_get;
+               bpf_obj_get_info_by_fd;
+               bpf_obj_pin;
+               bpf_object__btf_fd;
+               bpf_object__close;
+               bpf_object__find_map_by_name;
+               bpf_object__find_map_by_offset;
+               bpf_object__find_program_by_title;
+               bpf_object__kversion;
+               bpf_object__load;
+               bpf_object__name;
+               bpf_object__next;
+               bpf_object__open;
+               bpf_object__open_buffer;
+               bpf_object__open_xattr;
+               bpf_object__pin;
+               bpf_object__pin_maps;
+               bpf_object__pin_programs;
+               bpf_object__priv;
+               bpf_object__set_priv;
+               bpf_object__unload;
+               bpf_object__unpin_maps;
+               bpf_object__unpin_programs;
+               bpf_perf_event_read_simple;
+               bpf_prog_attach;
+               bpf_prog_detach;
+               bpf_prog_detach2;
+               bpf_prog_get_fd_by_id;
+               bpf_prog_get_next_id;
+               bpf_prog_load;
+               bpf_prog_load_xattr;
+               bpf_prog_query;
+               bpf_prog_test_run;
+               bpf_program__fd;
+               bpf_program__is_kprobe;
+               bpf_program__is_perf_event;
+               bpf_program__is_raw_tracepoint;
+               bpf_program__is_sched_act;
+               bpf_program__is_sched_cls;
+               bpf_program__is_socket_filter;
+               bpf_program__is_tracepoint;
+               bpf_program__is_xdp;
+               bpf_program__load;
+               bpf_program__next;
+               bpf_program__nth_fd;
+               bpf_program__pin;
+               bpf_program__pin_instance;
+               bpf_program__prev;
+               bpf_program__priv;
+               bpf_program__set_expected_attach_type;
+               bpf_program__set_ifindex;
+               bpf_program__set_kprobe;
+               bpf_program__set_perf_event;
+               bpf_program__set_prep;
+               bpf_program__set_priv;
+               bpf_program__set_raw_tracepoint;
+               bpf_program__set_sched_act;
+               bpf_program__set_sched_cls;
+               bpf_program__set_socket_filter;
+               bpf_program__set_tracepoint;
+               bpf_program__set_type;
+               bpf_program__set_xdp;
+               bpf_program__title;
+               bpf_program__unload;
+               bpf_program__unpin;
+               bpf_program__unpin_instance;
+               bpf_raw_tracepoint_open;
+               bpf_set_link_xdp_fd;
+               bpf_task_fd_query;
+               bpf_verify_program;
+               btf__fd;
+               btf__find_by_name;
+               btf__free;
+               btf__get_from_id;
+               btf__name_by_offset;
+               btf__new;
+               btf__resolve_size;
+               btf__resolve_type;
+               btf__type_by_id;
+               libbpf_attach_type_by_name;
+               libbpf_get_error;
+               libbpf_prog_type_by_name;
+               libbpf_set_print;
+               libbpf_strerror;
+       local:
+               *;
+};
index d83b17f..4343e40 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2017 Nicira, Inc.
  */
 
+#undef _GNU_SOURCE
 #include <stdio.h>
 #include <string.h>
 
diff --git a/tools/lib/bpf/test_libbpf.cpp b/tools/lib/bpf/test_libbpf.cpp
new file mode 100644 (file)
index 0000000..abf3fc2
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#include "libbpf.h"
+#include "bpf.h"
+#include "btf.h"
+
+/* do nothing, just make sure we can link successfully */
+
+int main(int argc, char *argv[])
+{
+    /* libbpf.h */
+    libbpf_set_print(NULL, NULL, NULL);
+
+    /* bpf.h */
+    bpf_prog_get_fd_by_id(0);
+
+    /* btf.h */
+    btf__new(NULL, 0, NULL);
+}
index e39dfb4..73aa6d8 100644 (file)
@@ -24,12 +24,13 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
        test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
        test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
        test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
-       test_netcnt
+       test_netcnt test_tcpnotify_user
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
        test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
        sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
        test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
+       test_tcpnotify_kern.o \
        sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \
        sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \
        test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \
@@ -37,7 +38,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
        test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
        get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
        test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o \
-       test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o
+       test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o \
+       xdp_dummy.o test_map_in_map.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -74,6 +76,7 @@ $(OUTPUT)/test_sock_addr: cgroup_helpers.c
 $(OUTPUT)/test_socket_cookie: cgroup_helpers.c
 $(OUTPUT)/test_sockmap: cgroup_helpers.c
 $(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
+$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
 $(OUTPUT)/test_progs: trace_helpers.c
 $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
 $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
@@ -124,7 +127,14 @@ $(OUTPUT)/test_stack_map.o: test_queue_stack_map.h
 BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
 BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
 BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
+BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
+                         $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
+                         readelf -S ./llvm_btf_verify.o | grep BTF; \
+                         /bin/rm -f ./llvm_btf_verify.o)
 
+ifneq ($(BTF_LLVM_PROBE),)
+       CLANG_FLAGS += -g
+else
 ifneq ($(BTF_LLC_PROBE),)
 ifneq ($(BTF_PAHOLE_PROBE),)
 ifneq ($(BTF_OBJCOPY_PROBE),)
@@ -134,6 +144,17 @@ ifneq ($(BTF_OBJCOPY_PROBE),)
 endif
 endif
 endif
+endif
+
+# Have one program compiled without "-target bpf" to test whether libbpf loads
+# it successfully
+$(OUTPUT)/test_xdp.o: test_xdp.c
+       $(CLANG) $(CLANG_FLAGS) \
+               -O2 -emit-llvm -c $< -o - | \
+       $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
+ifeq ($(DWARF2BTF),y)
+       $(BTF_PAHOLE) -J $@
+endif
 
 $(OUTPUT)/%.o: %.c
        $(CLANG) $(CLANG_FLAGS) \
index 107350a..b9798f5 100644 (file)
@@ -116,7 +116,7 @@ static __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto)
        return BPF_DROP;
 }
 
-SEC("dissect")
+SEC("flow_dissector")
 int _dissect(struct __sk_buff *skb)
 {
        if (!skb->vlan_present)
index efb6c13..d3eef9d 100644 (file)
@@ -113,6 +113,8 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
        (void *) BPF_FUNC_msg_pull_data;
 static int (*bpf_msg_push_data)(void *ctx, int start, int end, int flags) =
        (void *) BPF_FUNC_msg_push_data;
+static int (*bpf_msg_pop_data)(void *ctx, int start, int cut, int flags) =
+       (void *) BPF_FUNC_msg_pop_data;
 static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
        (void *) BPF_FUNC_bind;
 static int (*bpf_xdp_adjust_tail)(void *ctx, int offset) =
index 7f90d36..37f947e 100644 (file)
@@ -22,3 +22,4 @@ CONFIG_NET_CLS_FLOWER=m
 CONFIG_LWTUNNEL=y
 CONFIG_BPF_STREAM_PARSER=y
 CONFIG_XDP_SOCKETS=y
+CONFIG_FTRACE_SYSCALLS=y
index 5a88a68..b8395f3 100644 (file)
@@ -21,23 +21,48 @@ int _version SEC("version") = 1;
 SEC("cgroup/connect4")
 int connect_v4_prog(struct bpf_sock_addr *ctx)
 {
+       struct bpf_sock_tuple tuple = {};
        struct sockaddr_in sa;
+       struct bpf_sock *sk;
+
+       /* Verify that new destination is available. */
+       memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr));
+       memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport));
+
+       tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
+       tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
+
+       if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
+               return 0;
+       else if (ctx->type == SOCK_STREAM)
+               sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4), 0, 0);
+       else
+               sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4), 0, 0);
+
+       if (!sk)
+               return 0;
+
+       if (sk->src_ip4 != tuple.ipv4.daddr ||
+           sk->src_port != DST_REWRITE_PORT4) {
+               bpf_sk_release(sk);
+               return 0;
+       }
+
+       bpf_sk_release(sk);
 
        /* Rewrite destination. */
        ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
        ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
 
-       if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
-               ///* Rewrite source. */
-               memset(&sa, 0, sizeof(sa));
+       /* Rewrite source. */
+       memset(&sa, 0, sizeof(sa));
 
-               sa.sin_family = AF_INET;
-               sa.sin_port = bpf_htons(0);
-               sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
+       sa.sin_family = AF_INET;
+       sa.sin_port = bpf_htons(0);
+       sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
 
-               if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
-                       return 0;
-       }
+       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
+               return 0;
 
        return 1;
 }
index 8ea3f7d..25f5dc7 100644 (file)
@@ -29,7 +29,41 @@ int _version SEC("version") = 1;
 SEC("cgroup/connect6")
 int connect_v6_prog(struct bpf_sock_addr *ctx)
 {
+       struct bpf_sock_tuple tuple = {};
        struct sockaddr_in6 sa;
+       struct bpf_sock *sk;
+
+       /* Verify that new destination is available. */
+       memset(&tuple.ipv6.saddr, 0, sizeof(tuple.ipv6.saddr));
+       memset(&tuple.ipv6.sport, 0, sizeof(tuple.ipv6.sport));
+
+       tuple.ipv6.daddr[0] = bpf_htonl(DST_REWRITE_IP6_0);
+       tuple.ipv6.daddr[1] = bpf_htonl(DST_REWRITE_IP6_1);
+       tuple.ipv6.daddr[2] = bpf_htonl(DST_REWRITE_IP6_2);
+       tuple.ipv6.daddr[3] = bpf_htonl(DST_REWRITE_IP6_3);
+
+       tuple.ipv6.dport = bpf_htons(DST_REWRITE_PORT6);
+
+       if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
+               return 0;
+       else if (ctx->type == SOCK_STREAM)
+               sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv6), 0, 0);
+       else
+               sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv6), 0, 0);
+
+       if (!sk)
+               return 0;
+
+       if (sk->src_ip6[0] != tuple.ipv6.daddr[0] ||
+           sk->src_ip6[1] != tuple.ipv6.daddr[1] ||
+           sk->src_ip6[2] != tuple.ipv6.daddr[2] ||
+           sk->src_ip6[3] != tuple.ipv6.daddr[3] ||
+           sk->src_port != DST_REWRITE_PORT6) {
+               bpf_sk_release(sk);
+               return 0;
+       }
+
+       bpf_sk_release(sk);
 
        /* Rewrite destination. */
        ctx->user_ip6[0] = bpf_htonl(DST_REWRITE_IP6_0);
@@ -39,21 +73,19 @@ int connect_v6_prog(struct bpf_sock_addr *ctx)
 
        ctx->user_port = bpf_htons(DST_REWRITE_PORT6);
 
-       if (ctx->type == SOCK_DGRAM || ctx->type == SOCK_STREAM) {
-               /* Rewrite source. */
-               memset(&sa, 0, sizeof(sa));
+       /* Rewrite source. */
+       memset(&sa, 0, sizeof(sa));
 
-               sa.sin6_family = AF_INET6;
-               sa.sin6_port = bpf_htons(0);
+       sa.sin6_family = AF_INET6;
+       sa.sin6_port = bpf_htons(0);
 
-               sa.sin6_addr.s6_addr32[0] = bpf_htonl(SRC_REWRITE_IP6_0);
-               sa.sin6_addr.s6_addr32[1] = bpf_htonl(SRC_REWRITE_IP6_1);
-               sa.sin6_addr.s6_addr32[2] = bpf_htonl(SRC_REWRITE_IP6_2);
-               sa.sin6_addr.s6_addr32[3] = bpf_htonl(SRC_REWRITE_IP6_3);
+       sa.sin6_addr.s6_addr32[0] = bpf_htonl(SRC_REWRITE_IP6_0);
+       sa.sin6_addr.s6_addr32[1] = bpf_htonl(SRC_REWRITE_IP6_1);
+       sa.sin6_addr.s6_addr32[2] = bpf_htonl(SRC_REWRITE_IP6_2);
+       sa.sin6_addr.s6_addr32[3] = bpf_htonl(SRC_REWRITE_IP6_3);
 
-               if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
-                       return 0;
-       }
+       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
+               return 0;
 
        return 1;
 }
index 38e1cba..bded6c2 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/btf.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
+#include <linux/filter.h>
 #include <bpf/bpf.h>
 #include <sys/resource.h>
 #include <libelf.h>
@@ -22,6 +23,9 @@
 #include "bpf_rlimit.h"
 #include "bpf_util.h"
 
+#define MAX_INSNS      512
+#define MAX_SUBPROGS   16
+
 static uint32_t pass_cnt;
 static uint32_t error_cnt;
 static uint32_t skip_cnt;
@@ -85,8 +89,20 @@ static int __base_pr(const char *format, ...)
 #define BTF_TYPEDEF_ENC(name, type) \
        BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), type)
 
-#define BTF_PTR_ENC(name, type) \
-       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), type)
+#define BTF_PTR_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), type)
+
+#define BTF_CONST_ENC(type) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), type)
+
+#define BTF_FUNC_PROTO_ENC(ret_type, nargs) \
+       BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, nargs), ret_type)
+
+#define BTF_FUNC_PROTO_ARG_ENC(name, type) \
+       (name), (type)
+
+#define BTF_FUNC_ENC(name, func_proto) \
+       BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), func_proto)
 
 #define BTF_END_RAW 0xdeadbeef
 #define NAME_TBD 0xdeadb33f
@@ -103,6 +119,7 @@ static struct args {
        bool get_info_test;
        bool pprint_test;
        bool always_log;
+       bool func_type_test;
 } args;
 
 static char btf_log_buf[BTF_LOG_BUF_SIZE];
@@ -1735,6 +1752,464 @@ static struct btf_raw_test raw_tests[] = {
        .map_create_err = true,
 },
 
+{
+       .descr = "func proto (int (*)(int, unsigned int))",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* int (*)(int, unsigned int) */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func proto (vararg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int, unsigned int, ...) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func proto (vararg with name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b, ... c) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 0),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0c",
+       .str_sec_size = sizeof("\0a\0b\0c"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#3",
+},
+
+{
+       .descr = "func proto (arg after vararg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, ..., unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 3),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 0),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b",
+       .str_sec_size = sizeof("\0a\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func proto (CONST=>TYPEDEF=>PTR=>FUNC_PROTO)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* typedef void (*func_ptr)(int, unsigned int) */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),                   /* [3] */
+               /* const func_ptr */
+               BTF_CONST_ENC(3),                               /* [4] */
+               BTF_PTR_ENC(6),                                 /* [5] */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [6] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func_ptr",
+       .str_sec_size = sizeof("\0func_ptr"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func proto (CONST=>TYPEDEF=>FUNC_PROTO)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               BTF_CONST_ENC(4),                               /* [3] */
+               BTF_TYPEDEF_ENC(NAME_TBD, 5),                   /* [4] */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [5] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func_typedef",
+       .str_sec_size = sizeof("\0func_typedef"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid type_id",
+},
+
+{
+       .descr = "func proto (btf_resolve(arg))",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               /* void (*)(const void *) */
+               BTF_FUNC_PROTO_ENC(0, 1),                       /* [2] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 3),
+               BTF_CONST_ENC(4),                               /* [3] */
+               BTF_PTR_ENC(0),                                 /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func proto (Not all arg has name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0b",
+       .str_sec_size = sizeof("\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func proto (Bad arg name_off)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int <bad_name_off>) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0xffffffff, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a",
+       .str_sec_size = sizeof("\0a"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func proto (Bad arg name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int !!!) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0!!!",
+       .str_sec_size = sizeof("\0a\0!!!"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func proto (Invalid return type)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* <bad_ret_type> (*)(int, unsigned int) */
+               BTF_FUNC_PROTO_ENC(100, 2),                     /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid return type",
+},
+
+{
+       .descr = "func proto (with func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void func_proto(int, unsigned int) */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 2), 0),     /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func_proto",
+       .str_sec_size = sizeof("\0func_proto"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func proto (const void arg)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(const void) */
+               BTF_FUNC_PROTO_ENC(0, 1),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(0, 4),
+               BTF_CONST_ENC(0),                               /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "",
+       .str_sec_size = sizeof(""),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_proto_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#1",
+},
+
+{
+       .descr = "func (void func(int a, unsigned int b))",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void func(int a, unsigned int b) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0func",
+       .str_sec_size = sizeof("\0a\0b\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+},
+
+{
+       .descr = "func (No func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void <no_name>(int a, unsigned int b) */
+               BTF_FUNC_ENC(0, 3),                             /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b",
+       .str_sec_size = sizeof("\0a\0b"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func (Invalid func name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void !!!(int a, unsigned int b) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0!!!",
+       .str_sec_size = sizeof("\0a\0b\0!!!"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid name",
+},
+
+{
+       .descr = "func (Some arg has no name)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(0, 2),
+               /* void func(int a, unsigned int) */
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0func",
+       .str_sec_size = sizeof("\0a\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid arg#2",
+},
+
+{
+       .descr = "func (Non zero vlen)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_TYPE_INT_ENC(0, 0, 0, 32, 4),               /* [2] */
+               /* void (*)(int a, unsigned int b) */
+               BTF_FUNC_PROTO_ENC(0, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               /* void func(int a, unsigned int b) */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 2), 3),   /* [4] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0func",
+       .str_sec_size = sizeof("\0a\0b\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "vlen != 0",
+},
+
+{
+       .descr = "func (Not referring to FUNC_PROTO)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
+               BTF_FUNC_ENC(NAME_TBD, 1),                      /* [2] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0func",
+       .str_sec_size = sizeof("\0func"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "func_type_check_btf",
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .key_type_id = 1,
+       .value_type_id = 1,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid type_id",
+},
+
 }; /* struct btf_raw_test raw_tests[] */
 
 static const char *get_next_str(const char *start, const char *end)
@@ -2301,13 +2776,13 @@ static struct btf_file_test file_tests[] = {
 },
 };
 
-static int file_has_btf_elf(const char *fn)
+static int file_has_btf_elf(const char *fn, bool *has_btf_ext)
 {
        Elf_Scn *scn = NULL;
        GElf_Ehdr ehdr;
+       int ret = 0;
        int elf_fd;
        Elf *elf;
-       int ret;
 
        if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
                  "elf_version(EV_CURRENT) == EV_NONE"))
@@ -2339,14 +2814,12 @@ static int file_has_btf_elf(const char *fn)
                }
 
                sh_name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
-               if (!strcmp(sh_name, BTF_ELF_SEC)) {
+               if (!strcmp(sh_name, BTF_ELF_SEC))
                        ret = 1;
-                       goto done;
-               }
+               if (!strcmp(sh_name, BTF_EXT_ELF_SEC))
+                       *has_btf_ext = true;
        }
 
-       ret = 0;
-
 done:
        close(elf_fd);
        elf_end(elf);
@@ -2356,15 +2829,24 @@ done:
 static int do_test_file(unsigned int test_num)
 {
        const struct btf_file_test *test = &file_tests[test_num - 1];
+       const char *expected_fnames[] = {"_dummy_tracepoint",
+                                        "test_long_fname_1",
+                                        "test_long_fname_2"};
+       struct bpf_prog_info info = {};
        struct bpf_object *obj = NULL;
+       struct bpf_func_info *finfo;
        struct bpf_program *prog;
+       __u32 info_len, rec_size;
+       bool has_btf_ext = false;
+       struct btf *btf = NULL;
+       void *func_info = NULL;
        struct bpf_map *map;
-       int err;
+       int i, err, prog_fd;
 
        fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
                test->file);
 
-       err = file_has_btf_elf(test->file);
+       err = file_has_btf_elf(test->file, &has_btf_ext);
        if (err == -1)
                return err;
 
@@ -2392,6 +2874,7 @@ static int do_test_file(unsigned int test_num)
        err = bpf_object__load(obj);
        if (CHECK(err < 0, "bpf_object__load: %d", err))
                goto done;
+       prog_fd = bpf_program__fd(prog);
 
        map = bpf_object__find_map_by_name(obj, "btf_map");
        if (CHECK(!map, "btf_map not found")) {
@@ -2406,9 +2889,100 @@ static int do_test_file(unsigned int test_num)
                  test->btf_kv_notfound))
                goto done;
 
+       if (!has_btf_ext)
+               goto skip;
+
+       /* get necessary program info */
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+
+       if (CHECK(err == -1, "invalid get info (1st) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_cnt != 3,
+                 "incorrect info.func_info_cnt (1st) %d",
+                 info.func_info_cnt)) {
+               err = -1;
+               goto done;
+       }
+       rec_size = info.func_info_rec_size;
+       if (CHECK(rec_size < 4,
+                 "incorrect info.func_info_rec_size (1st) %d\n", rec_size)) {
+               err = -1;
+               goto done;
+       }
+
+       func_info = malloc(info.func_info_cnt * rec_size);
+       if (CHECK(!func_info, "out of memory")) {
+               err = -1;
+               goto done;
+       }
+
+       /* reset info to only retrieve func_info related data */
+       memset(&info, 0, sizeof(info));
+       info.func_info_cnt = 3;
+       info.func_info_rec_size = rec_size;
+       info.func_info = ptr_to_u64(func_info);
+
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+
+       if (CHECK(err == -1, "invalid get info (2nd) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_cnt != 3,
+                 "incorrect info.func_info_cnt (2nd) %d",
+                 info.func_info_cnt)) {
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_rec_size != rec_size,
+                 "incorrect info.func_info_rec_size (2nd) %d",
+                 info.func_info_rec_size)) {
+               err = -1;
+               goto done;
+       }
+
+       err = btf__get_from_id(info.btf_id, &btf);
+       if (CHECK(err, "cannot get btf from kernel, err: %d", err))
+               goto done;
+
+       /* check three functions */
+       finfo = func_info;
+       for (i = 0; i < 3; i++) {
+               const struct btf_type *t;
+               const char *fname;
+
+               t = btf__type_by_id(btf, finfo->type_id);
+               if (CHECK(!t, "btf__type_by_id failure: id %u",
+                         finfo->type_id)) {
+                       err = -1;
+                       goto done;
+               }
+
+               fname = btf__name_by_offset(btf, t->name_off);
+               err = strcmp(fname, expected_fnames[i]);
+               /* for the second and third functions in .text section,
+                * the compiler may order them either way.
+                */
+               if (i && err)
+                       err = strcmp(fname, expected_fnames[3 - i]);
+               if (CHECK(err, "incorrect fname %s", fname ? : "")) {
+                       err = -1;
+                       goto done;
+               }
+
+               finfo = (void *)finfo + rec_size;
+       }
+
+skip:
        fprintf(stderr, "OK");
 
 done:
+       free(func_info);
        bpf_object__close(obj);
        return err;
 }
@@ -2838,16 +3412,304 @@ static int test_pprint(void)
        return err;
 }
 
+static struct btf_func_type_test {
+       const char *descr;
+       const char *str_sec;
+       __u32 raw_types[MAX_NR_RAW_TYPES];
+       __u32 str_sec_size;
+       struct bpf_insn insns[MAX_INSNS];
+       __u32 prog_type;
+       __u32 func_info[MAX_SUBPROGS][2];
+       __u32 func_info_rec_size;
+       __u32 func_info_cnt;
+       bool expected_prog_load_failure;
+} func_type_test[] = {
+{
+       .descr = "func_type (main func + one sub)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 2,
+},
+
+{
+       .descr = "func_type (Incorrect func_info_rec_size)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 4,
+       .func_info_cnt = 2,
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "func_type (Incorrect func_info_cnt)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {3, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 1,
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "func_type (Incorrect bpf_func_info.insn_offset)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4),        /* [2] */
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [3] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+               BTF_FUNC_PROTO_ENC(1, 2),                       /* [4] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2),
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 3),                      /* [5] */
+               BTF_FUNC_ENC(NAME_TBD, 4),                      /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB",
+       .str_sec_size = sizeof("\0int\0unsigned int\0a\0b\0c\0d\0funcA\0funcB"),
+       .insns = {
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info = { {0, 5}, {2, 6} },
+       .func_info_rec_size = 8,
+       .func_info_cnt = 2,
+       .expected_prog_load_failure = true,
+},
+
+};
+
+static size_t probe_prog_length(const struct bpf_insn *fp)
+{
+       size_t len;
+
+       for (len = MAX_INSNS - 1; len > 0; --len)
+               if (fp[len].code != 0 || fp[len].imm != 0)
+                       break;
+       return len + 1;
+}
+
+static int do_test_func_type(int test_num)
+{
+       const struct btf_func_type_test *test = &func_type_test[test_num];
+       unsigned int raw_btf_size, info_len, rec_size;
+       int i, btf_fd = -1, prog_fd = -1, err = 0;
+       struct bpf_load_program_attr attr = {};
+       void *raw_btf, *func_info = NULL;
+       struct bpf_prog_info info = {};
+       struct bpf_func_info *finfo;
+
+       fprintf(stderr, "%s......", test->descr);
+       raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
+                                test->str_sec, test->str_sec_size,
+                                &raw_btf_size);
+
+       if (!raw_btf)
+               return -1;
+
+       *btf_log_buf = '\0';
+       btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
+                             btf_log_buf, BTF_LOG_BUF_SIZE,
+                             args.always_log);
+       free(raw_btf);
+
+       if (CHECK(btf_fd == -1, "invalid btf_fd errno:%d", errno)) {
+               err = -1;
+               goto done;
+       }
+
+       if (*btf_log_buf && args.always_log)
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       attr.prog_type = test->prog_type;
+       attr.insns = test->insns;
+       attr.insns_cnt = probe_prog_length(attr.insns);
+       attr.license = "GPL";
+       attr.prog_btf_fd = btf_fd;
+       attr.func_info_rec_size = test->func_info_rec_size;
+       attr.func_info_cnt = test->func_info_cnt;
+       attr.func_info = test->func_info;
+
+       *btf_log_buf = '\0';
+       prog_fd = bpf_load_program_xattr(&attr, btf_log_buf,
+                                        BTF_LOG_BUF_SIZE);
+       if (test->expected_prog_load_failure && prog_fd == -1) {
+               err = 0;
+               goto done;
+       }
+       if (CHECK(prog_fd == -1, "invalid prog_id errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+
+       /* get necessary lens */
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "invalid get info (1st) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_cnt != 2,
+                 "incorrect info.func_info_cnt (1st) %d\n",
+                 info.func_info_cnt)) {
+               err = -1;
+               goto done;
+       }
+       rec_size = info.func_info_rec_size;
+       if (CHECK(rec_size < 4,
+                 "incorrect info.func_info_rec_size (1st) %d\n", rec_size)) {
+               err = -1;
+               goto done;
+       }
+
+       func_info = malloc(info.func_info_cnt * rec_size);
+       if (CHECK(!func_info, "out of memory")) {
+               err = -1;
+               goto done;
+       }
+
+       /* reset info to only retrieve func_info related data */
+       memset(&info, 0, sizeof(info));
+       info.func_info_cnt = 2;
+       info.func_info_rec_size = rec_size;
+       info.func_info = ptr_to_u64(func_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "invalid get info (2nd) errno:%d", errno)) {
+               fprintf(stderr, "%s\n", btf_log_buf);
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_cnt != 2,
+                 "incorrect info.func_info_cnt (2nd) %d\n",
+                 info.func_info_cnt)) {
+               err = -1;
+               goto done;
+       }
+       if (CHECK(info.func_info_rec_size != rec_size,
+                 "incorrect info.func_info_rec_size (2nd) %d\n",
+                 info.func_info_rec_size)) {
+               err = -1;
+               goto done;
+       }
+
+       finfo = func_info;
+       for (i = 0; i < 2; i++) {
+               if (CHECK(finfo->type_id != test->func_info[i][1],
+                         "incorrect func_type %u expected %u",
+                         finfo->type_id, test->func_info[i][1])) {
+                       err = -1;
+                       goto done;
+               }
+               finfo = (void *)finfo + rec_size;
+       }
+
+done:
+       if (*btf_log_buf && (err || args.always_log))
+               fprintf(stderr, "\n%s", btf_log_buf);
+
+       if (btf_fd != -1)
+               close(btf_fd);
+       if (prog_fd != -1)
+               close(prog_fd);
+       free(func_info);
+       return err;
+}
+
+static int test_func_type(void)
+{
+       unsigned int i;
+       int err = 0;
+
+       for (i = 0; i < ARRAY_SIZE(func_type_test); i++)
+               err |= count_result(do_test_func_type(i));
+
+       return err;
+}
+
 static void usage(const char *cmd)
 {
-       fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] | [-g test_num (1 - %zu)] | [-f test_num (1 - %zu)] | [-p]]\n",
+       fprintf(stderr, "Usage: %s [-l] [[-r test_num (1 - %zu)] |"
+                       " [-g test_num (1 - %zu)] |"
+                       " [-f test_num (1 - %zu)] | [-p] | [-k] ]\n",
                cmd, ARRAY_SIZE(raw_tests), ARRAY_SIZE(get_info_tests),
                ARRAY_SIZE(file_tests));
 }
 
 static int parse_args(int argc, char **argv)
 {
-       const char *optstr = "lpf:r:g:";
+       const char *optstr = "lpkf:r:g:";
        int opt;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
@@ -2870,6 +3732,9 @@ static int parse_args(int argc, char **argv)
                case 'p':
                        args.pprint_test = true;
                        break;
+               case 'k':
+                       args.func_type_test = true;
+                       break;
                case 'h':
                        usage(argv[0]);
                        exit(0);
@@ -2935,8 +3800,11 @@ int main(int argc, char **argv)
        if (args.pprint_test)
                err |= test_pprint();
 
+       if (args.func_type_test)
+               err |= test_func_type();
+
        if (args.raw_test || args.get_info_test || args.file_test ||
-           args.pprint_test)
+           args.pprint_test || args.func_type_test)
                goto done;
 
        err |= test_raw();
index b21b876..e5c79fe 100644 (file)
@@ -24,8 +24,8 @@ struct dummy_tracepoint_args {
        struct sock *sock;
 };
 
-SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
 {
        struct ipv_counts *counts;
        int key = 0;
@@ -42,4 +42,16 @@ int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
        return 0;
 }
 
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_1(arg);
+}
+
 char _license[] SEC("license") = "GPL";
index 0ed8e08..434188c 100644 (file)
@@ -22,8 +22,8 @@ struct dummy_tracepoint_args {
        struct sock *sock;
 };
 
-SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
 {
        struct ipv_counts *counts;
        int key = 0;
@@ -40,4 +40,16 @@ int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
        return 0;
 }
 
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+       return test_long_fname_1(arg);
+}
+
 char _license[] SEC("license") = "GPL";
index c0fb073..d23d4da 100755 (executable)
@@ -59,7 +59,7 @@ else
 fi
 
 # Attach BPF program
-./flow_dissector_load -p bpf_flow.o -s dissect
+./flow_dissector_load -p bpf_flow.o -s flow_dissector
 
 # Setup
 tc qdisc add dev lo ingress
index 156d89f..2989b2e 100755 (executable)
@@ -33,17 +33,11 @@ trap exit_handler 0 2 3 6 9
 
 libbpf_open_file test_l4lb.o
 
-# TODO: fix libbpf to load noinline functions
-# [warning] libbpf: incorrect bpf_call opcode
-#libbpf_open_file test_l4lb_noinline.o
+# Load a program with BPF-to-BPF calls
+libbpf_open_file test_l4lb_noinline.o
 
-# TODO: fix test_xdp_meta.c to load with libbpf
-# [warning] libbpf: test_xdp_meta.o doesn't provide kernel version
-#libbpf_open_file test_xdp_meta.o
-
-# TODO: fix libbpf to handle .eh_frame
-# [warning] libbpf: relocation failed: no section(10)
-#libbpf_open_file ../../../../samples/bpf/tracex3_kern.o
+# Load a program compiled without the "-target bpf" flag
+libbpf_open_file test_xdp.o
 
 # Success
 exit 0
diff --git a/tools/testing/selftests/bpf/test_map_in_map.c b/tools/testing/selftests/bpf/test_map_in_map.c
new file mode 100644 (file)
index 0000000..ce923e6
--- /dev/null
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <linux/types.h>
+#include "bpf_helpers.h"
+
+struct bpf_map_def SEC("maps") mim_array = {
+       .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
+       .key_size = sizeof(int),
+       /* must be sizeof(__u32) for map in map */
+       .value_size = sizeof(__u32),
+       .max_entries = 1,
+       .map_flags = 0,
+};
+
+struct bpf_map_def SEC("maps") mim_hash = {
+       .type = BPF_MAP_TYPE_HASH_OF_MAPS,
+       .key_size = sizeof(int),
+       /* must be sizeof(__u32) for map in map */
+       .value_size = sizeof(__u32),
+       .max_entries = 1,
+       .map_flags = 0,
+};
+
+SEC("xdp_mimtest")
+int xdp_mimtest0(struct xdp_md *ctx)
+{
+       int value = 123;
+       int key = 0;
+       void *map;
+
+       map = bpf_map_lookup_elem(&mim_array, &key);
+       if (!map)
+               return XDP_DROP;
+
+       bpf_map_update_elem(map, &key, &value, 0);
+
+       map = bpf_map_lookup_elem(&mim_hash, &key);
+       if (!map)
+               return XDP_DROP;
+
+       bpf_map_update_elem(map, &key, &value, 0);
+
+       return XDP_PASS;
+}
+
+int _version SEC("version") = 1;
+char _license[] SEC("license") = "GPL";
index 4db2116..9c79ee0 100644 (file)
@@ -258,24 +258,36 @@ static void test_hashmap_percpu(int task, void *data)
        close(fd);
 }
 
-static void test_hashmap_walk(int task, void *data)
+static int helper_fill_hashmap(int max_entries)
 {
-       int fd, i, max_entries = 1000;
-       long long key, value, next_key;
-       bool next_key_valid = true;
+       int i, fd, ret;
+       long long key, value;
 
        fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
                            max_entries, map_flags);
-       if (fd < 0) {
-               printf("Failed to create hashmap '%s'!\n", strerror(errno));
-               exit(1);
-       }
+       CHECK(fd < 0,
+             "failed to create hashmap",
+             "err: %s, flags: 0x%x\n", strerror(errno), map_flags);
 
        for (i = 0; i < max_entries; i++) {
                key = i; value = key;
-               assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
+               ret = bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST);
+               CHECK(ret != 0,
+                     "can't update hashmap",
+                     "err: %s\n", strerror(ret));
        }
 
+       return fd;
+}
+
+static void test_hashmap_walk(int task, void *data)
+{
+       int fd, i, max_entries = 1000;
+       long long key, value, next_key;
+       bool next_key_valid = true;
+
+       fd = helper_fill_hashmap(max_entries);
+
        for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
                                         &next_key) == 0; i++) {
                key = next_key;
@@ -306,6 +318,39 @@ static void test_hashmap_walk(int task, void *data)
        close(fd);
 }
 
+static void test_hashmap_zero_seed(void)
+{
+       int i, first, second, old_flags;
+       long long key, next_first, next_second;
+
+       old_flags = map_flags;
+       map_flags |= BPF_F_ZERO_SEED;
+
+       first = helper_fill_hashmap(3);
+       second = helper_fill_hashmap(3);
+
+       for (i = 0; ; i++) {
+               void *key_ptr = !i ? NULL : &key;
+
+               if (bpf_map_get_next_key(first, key_ptr, &next_first) != 0)
+                       break;
+
+               CHECK(bpf_map_get_next_key(second, key_ptr, &next_second) != 0,
+                     "next_key for second map must succeed",
+                     "key_ptr: %p", key_ptr);
+               CHECK(next_first != next_second,
+                     "keys must match",
+                     "i: %d first: %lld second: %lld\n", i,
+                     next_first, next_second);
+
+               key = next_first;
+       }
+
+       map_flags = old_flags;
+       close(first);
+       close(second);
+}
+
 static void test_arraymap(int task, void *data)
 {
        int key, next_key, fd;
@@ -1080,6 +1125,94 @@ out_sockmap:
        exit(1);
 }
 
+#define MAPINMAP_PROG "./test_map_in_map.o"
+static void test_map_in_map(void)
+{
+       struct bpf_program *prog;
+       struct bpf_object *obj;
+       struct bpf_map *map;
+       int mim_fd, fd, err;
+       int pos = 0;
+
+       obj = bpf_object__open(MAPINMAP_PROG);
+
+       fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int),
+                           2, 0);
+       if (fd < 0) {
+               printf("Failed to create hashmap '%s'!\n", strerror(errno));
+               exit(1);
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_array");
+       if (IS_ERR(map)) {
+               printf("Failed to load array of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       err = bpf_map__set_inner_map_fd(map, fd);
+       if (err) {
+               printf("Failed to set inner_map_fd for array of maps\n");
+               goto out_map_in_map;
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_hash");
+       if (IS_ERR(map)) {
+               printf("Failed to load hash of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       err = bpf_map__set_inner_map_fd(map, fd);
+       if (err) {
+               printf("Failed to set inner_map_fd for hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       bpf_object__for_each_program(prog, obj) {
+               bpf_program__set_xdp(prog);
+       }
+       bpf_object__load(obj);
+
+       map = bpf_object__find_map_by_name(obj, "mim_array");
+       if (IS_ERR(map)) {
+               printf("Failed to load array of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       mim_fd = bpf_map__fd(map);
+       if (mim_fd < 0) {
+               printf("Failed to get descriptor for array of maps\n");
+               goto out_map_in_map;
+       }
+
+       err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
+       if (err) {
+               printf("Failed to update array of maps\n");
+               goto out_map_in_map;
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim_hash");
+       if (IS_ERR(map)) {
+               printf("Failed to load hash of maps from test prog\n");
+               goto out_map_in_map;
+       }
+       mim_fd = bpf_map__fd(map);
+       if (mim_fd < 0) {
+               printf("Failed to get descriptor for hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
+       if (err) {
+               printf("Failed to update hash of maps\n");
+               goto out_map_in_map;
+       }
+
+       close(fd);
+       bpf_object__close(obj);
+       return;
+
+out_map_in_map:
+       close(fd);
+       exit(1);
+}
+
 #define MAP_SIZE (32 * 1024)
 
 static void test_map_large(void)
@@ -1534,6 +1667,7 @@ static void run_all_tests(void)
        test_hashmap(0, NULL);
        test_hashmap_percpu(0, NULL);
        test_hashmap_walk(0, NULL);
+       test_hashmap_zero_seed();
 
        test_arraymap(0, NULL);
        test_arraymap_percpu(0, NULL);
@@ -1554,6 +1688,8 @@ static void run_all_tests(void)
 
        test_queuemap(0, NULL);
        test_stackmap(0, NULL);
+
+       test_map_in_map();
 }
 
 int main(void)
index 2d3c04f..1c57abb 100644 (file)
@@ -524,7 +524,7 @@ static void test_bpf_obj_id(void)
                          load_time < now - 60 || load_time > now + 60 ||
                          prog_infos[i].created_by_uid != my_uid ||
                          prog_infos[i].nr_map_ids != 1 ||
-                         *(int *)prog_infos[i].map_ids != map_infos[i].id ||
+                         *(int *)(long)prog_infos[i].map_ids != map_infos[i].id ||
                          strcmp((char *)prog_infos[i].name, expected_prog_name),
                          "get-prog-info(fd)",
                          "err %d errno %d i %d type %d(%d) info_len %u(%Zu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d load_time %lu(%lu) uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) name %s(%s)\n",
@@ -539,7 +539,7 @@ static void test_bpf_obj_id(void)
                          load_time, now,
                          prog_infos[i].created_by_uid, my_uid,
                          prog_infos[i].nr_map_ids, 1,
-                         *(int *)prog_infos[i].map_ids, map_infos[i].id,
+                         *(int *)(long)prog_infos[i].map_ids, map_infos[i].id,
                          prog_infos[i].name, expected_prog_name))
                        goto done;
        }
@@ -585,7 +585,7 @@ static void test_bpf_obj_id(void)
                bzero(&prog_info, sizeof(prog_info));
                info_len = sizeof(prog_info);
 
-               saved_map_id = *(int *)(prog_infos[i].map_ids);
+               saved_map_id = *(int *)((long)prog_infos[i].map_ids);
                prog_info.map_ids = prog_infos[i].map_ids;
                prog_info.nr_map_ids = 2;
                err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
@@ -593,12 +593,12 @@ static void test_bpf_obj_id(void)
                prog_infos[i].xlated_prog_insns = 0;
                CHECK(err || info_len != sizeof(struct bpf_prog_info) ||
                      memcmp(&prog_info, &prog_infos[i], info_len) ||
-                     *(int *)prog_info.map_ids != saved_map_id,
+                     *(int *)(long)prog_info.map_ids != saved_map_id,
                      "get-prog-info(next_id->fd)",
                      "err %d errno %d info_len %u(%Zu) memcmp %d map_id %u(%u)\n",
                      err, errno, info_len, sizeof(struct bpf_prog_info),
                      memcmp(&prog_info, &prog_infos[i], info_len),
-                     *(int *)prog_info.map_ids, saved_map_id);
+                     *(int *)(long)prog_info.map_ids, saved_map_id);
                close(prog_fd);
        }
        CHECK(nr_id_found != nr_iters,
@@ -1703,7 +1703,7 @@ static void test_reference_tracking()
        const char *file = "./test_sk_lookup_kern.o";
        struct bpf_object *obj;
        struct bpf_program *prog;
-       __u32 duration;
+       __u32 duration = 0;
        int err = 0;
 
        obj = bpf_object__open(file);
index aeeb76a..73b7493 100644 (file)
@@ -574,24 +574,44 @@ static int bind4_prog_load(const struct sock_addr_test *test)
                /* if (sk.family == AF_INET && */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, family)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 16),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 24),
 
                /*     (sk.type == SOCK_DGRAM || sk.type == SOCK_STREAM) && */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, type)),
                BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 1),
                BPF_JMP_A(1),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 12),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_STREAM, 20),
 
                /*     1st_byte_of_user_ip4 == expected && */
                BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, user_ip4)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 10),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[0], 18),
+
+               /*     2nd_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 1),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[1], 16),
+
+               /*     3rd_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 2),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[2], 14),
+
+               /*     4th_byte_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 3),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr8[3], 12),
 
                /*     1st_half_of_user_ip4 == expected && */
                BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
                            offsetof(struct bpf_sock_addr, user_ip4)),
-               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 8),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[0], 10),
+
+               /*     2nd_half_of_user_ip4 == expected && */
+               BPF_LDX_MEM(BPF_H, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, user_ip4) + 2),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, ip4.u4_addr16[1], 8),
 
                /*     whole_user_ip4 == expected) { */
                BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
index 622ade0..e85a771 100644 (file)
@@ -79,6 +79,8 @@ int txmsg_start;
 int txmsg_end;
 int txmsg_start_push;
 int txmsg_end_push;
+int txmsg_start_pop;
+int txmsg_pop;
 int txmsg_ingress;
 int txmsg_skb;
 int ktls;
@@ -104,6 +106,8 @@ static const struct option long_options[] = {
        {"txmsg_end",   required_argument,      NULL, 'e'},
        {"txmsg_start_push", required_argument, NULL, 'p'},
        {"txmsg_end_push",   required_argument, NULL, 'q'},
+       {"txmsg_start_pop",  required_argument, NULL, 'w'},
+       {"txmsg_pop",        required_argument, NULL, 'x'},
        {"txmsg_ingress", no_argument,          &txmsg_ingress, 1 },
        {"txmsg_skb", no_argument,              &txmsg_skb, 1 },
        {"ktls", no_argument,                   &ktls, 1 },
@@ -473,13 +477,27 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                clock_gettime(CLOCK_MONOTONIC, &s->end);
        } else {
                int slct, recvp = 0, recv, max_fd = fd;
+               float total_bytes, txmsg_pop_total;
                int fd_flags = O_NONBLOCK;
                struct timeval timeout;
-               float total_bytes;
                fd_set w;
 
                fcntl(fd, fd_flags);
+               /* Account for pop bytes noting each iteration of apply will
+                * call msg_pop_data helper so we need to account for this
+                * by calculating the number of apply iterations. Note user
+                * of the tool can create cases where no data is sent by
+                * manipulating pop/push/pull/etc. For example txmsg_apply 1
+                * with txmsg_pop 1 will try to apply 1B at a time but each
+                * iteration will then pop 1B so no data will ever be sent.
+                * This is really only useful for testing edge cases in code
+                * paths.
+                */
                total_bytes = (float)iov_count * (float)iov_length * (float)cnt;
+               txmsg_pop_total = txmsg_pop;
+               if (txmsg_apply)
+                       txmsg_pop_total *= (total_bytes / txmsg_apply);
+               total_bytes -= txmsg_pop_total;
                err = clock_gettime(CLOCK_MONOTONIC, &s->start);
                if (err < 0)
                        perror("recv start time: ");
@@ -488,7 +506,7 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                                timeout.tv_sec = 0;
                                timeout.tv_usec = 300000;
                        } else {
-                               timeout.tv_sec = 1;
+                               timeout.tv_sec = 3;
                                timeout.tv_usec = 0;
                        }
 
@@ -503,7 +521,7 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
                                goto out_errno;
                        } else if (!slct) {
                                if (opt->verbose)
-                                       fprintf(stderr, "unexpected timeout\n");
+                                       fprintf(stderr, "unexpected timeout: recved %zu/%f pop_total %f\n", s->bytes_recvd, total_bytes, txmsg_pop_total);
                                errno = -EIO;
                                clock_gettime(CLOCK_MONOTONIC, &s->end);
                                goto out_errno;
@@ -619,7 +637,7 @@ static int sendmsg_test(struct sockmap_options *opt)
                        iov_count = 1;
                err = msg_loop(rx_fd, iov_count, iov_buf,
                               cnt, &s, false, opt);
-               if (err && opt->verbose)
+               if (opt->verbose)
                        fprintf(stderr,
                                "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n",
                                iov_count, iov_buf, cnt, err);
@@ -931,6 +949,39 @@ run:
                        }
                }
 
+               if (txmsg_start_pop) {
+                       i = 4;
+                       err = bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_start_pop, BPF_ANY);
+                       if (err) {
+                               fprintf(stderr,
+                                       "ERROR: bpf_map_update_elem %i@%i (txmsg_start_pop):  %d (%s)\n",
+                                       txmsg_start_pop, i, err, strerror(errno));
+                               goto out;
+                       }
+               } else {
+                       i = 4;
+                       bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_start_pop, BPF_ANY);
+               }
+
+               if (txmsg_pop) {
+                       i = 5;
+                       err = bpf_map_update_elem(map_fd[5],
+                                                 &i, &txmsg_pop, BPF_ANY);
+                       if (err) {
+                               fprintf(stderr,
+                                       "ERROR: bpf_map_update_elem %i@%i (txmsg_pop):  %d (%s)\n",
+                                       txmsg_pop, i, err, strerror(errno));
+                               goto out;
+                       }
+               } else {
+                       i = 5;
+                       bpf_map_update_elem(map_fd[5],
+                                           &i, &txmsg_pop, BPF_ANY);
+
+               }
+
                if (txmsg_ingress) {
                        int in = BPF_F_INGRESS;
 
@@ -1082,6 +1133,11 @@ static void test_options(char *options)
                snprintf(tstr, OPTSTRING, "end %d,", txmsg_end);
                strncat(options, tstr, OPTSTRING);
        }
+       if (txmsg_start_pop) {
+               snprintf(tstr, OPTSTRING, "pop (%d,%d),",
+                        txmsg_start_pop, txmsg_start_pop + txmsg_pop);
+               strncat(options, tstr, OPTSTRING);
+       }
        if (txmsg_ingress)
                strncat(options, "ingress,", OPTSTRING);
        if (txmsg_skb)
@@ -1264,6 +1320,7 @@ static int test_mixed(int cgrp)
        txmsg_apply = txmsg_cork = 0;
        txmsg_start = txmsg_end = 0;
        txmsg_start_push = txmsg_end_push = 0;
+       txmsg_start_pop = txmsg_pop = 0;
 
        /* Test small and large iov_count values with pass/redir/apply/cork */
        txmsg_pass = 1;
@@ -1383,6 +1440,19 @@ static int test_start_end(int cgrp)
        txmsg_end = 2;
        txmsg_start_push = 1;
        txmsg_end_push = 2;
+       txmsg_start_pop = 1;
+       txmsg_pop = 1;
+       err = test_txmsg(cgrp);
+       if (err)
+               goto out;
+
+       /* Cut a byte of pushed data but leave reamining in place */
+       txmsg_start = 1;
+       txmsg_end = 2;
+       txmsg_start_push = 1;
+       txmsg_end_push = 3;
+       txmsg_start_pop = 1;
+       txmsg_pop = 1;
        err = test_txmsg(cgrp);
        if (err)
                goto out;
@@ -1393,6 +1463,9 @@ static int test_start_end(int cgrp)
        opt.iov_length = 100;
        txmsg_cork = 1600;
 
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        for (i = 99; i <= 1600; i += 500) {
                txmsg_start = 0;
                txmsg_end = i;
@@ -1403,6 +1476,17 @@ static int test_start_end(int cgrp)
                        goto out;
        }
 
+       /* Test pop data in middle of cork */
+       for (i = 99; i <= 1600; i += 500) {
+               txmsg_start_pop = 10;
+               txmsg_pop = i;
+               err = test_exec(cgrp, &opt);
+               if (err)
+                       goto out;
+       }
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        /* Test start/end with cork but pull data in middle */
        for (i = 199; i <= 1600; i += 500) {
                txmsg_start = 100;
@@ -1423,6 +1507,15 @@ static int test_start_end(int cgrp)
        if (err)
                goto out;
 
+       /* Test pop with cork pulling last sg entry */
+       txmsg_start_pop = 1500;
+       txmsg_pop = 1600;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+       txmsg_start_pop = 0;
+       txmsg_pop = 0;
+
        /* Test start/end pull of single byte in last page */
        txmsg_start = 1111;
        txmsg_end = 1112;
@@ -1432,6 +1525,13 @@ static int test_start_end(int cgrp)
        if (err)
                goto out;
 
+       /* Test pop of single byte in last page */
+       txmsg_start_pop = 1111;
+       txmsg_pop = 1112;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+
        /* Test start/end with end < start */
        txmsg_start = 1111;
        txmsg_end = 0;
@@ -1456,7 +1556,20 @@ static int test_start_end(int cgrp)
        txmsg_start_push = 1601;
        txmsg_end_push = 1600;
        err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
+
+       /* Test pop with start > data */
+       txmsg_start_pop = 1601;
+       txmsg_pop = 1;
+       err = test_exec(cgrp, &opt);
+       if (err)
+               goto out;
 
+       /* Test pop with pop range > data */
+       txmsg_start_pop = 1599;
+       txmsg_pop = 10;
+       err = test_exec(cgrp, &opt);
 out:
        txmsg_start = 0;
        txmsg_end = 0;
@@ -1641,6 +1754,12 @@ int main(int argc, char **argv)
                case 'q':
                        txmsg_end_push = atoi(optarg);
                        break;
+               case 'w':
+                       txmsg_start_pop = atoi(optarg);
+                       break;
+               case 'x':
+                       txmsg_pop = atoi(optarg);
+                       break;
                case 'a':
                        txmsg_apply = atoi(optarg);
                        break;
index 14b8bba..e7639f6 100644 (file)
@@ -74,7 +74,7 @@ struct bpf_map_def SEC("maps") sock_bytes = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(int),
        .value_size = sizeof(int),
-       .max_entries = 4
+       .max_entries = 6
 };
 
 struct bpf_map_def SEC("maps") sock_redir_flags = {
@@ -181,8 +181,8 @@ int bpf_sockmap(struct bpf_sock_ops *skops)
 SEC("sk_msg1")
 int bpf_prog4(struct sk_msg_md *msg)
 {
-       int *bytes, zero = 0, one = 1, two = 2, three = 3;
-       int *start, *end, *start_push, *end_push;
+       int *bytes, zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int *start, *end, *start_push, *end_push, *start_pop, *pop;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
        if (bytes)
@@ -198,15 +198,19 @@ int bpf_prog4(struct sk_msg_md *msg)
        end_push = bpf_map_lookup_elem(&sock_bytes, &three);
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
        return SK_PASS;
 }
 
 SEC("sk_msg2")
 int bpf_prog5(struct sk_msg_md *msg)
 {
-       int zero = 0, one = 1, two = 2, three = 3;
-       int *start, *end, *start_push, *end_push;
-       int *bytes, len1, len2 = 0, len3;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int *start, *end, *start_push, *end_push, *start_pop, *pop;
+       int *bytes, len1, len2 = 0, len3, len4;
        int err1 = -1, err2 = -1;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
@@ -247,6 +251,20 @@ int bpf_prog5(struct sk_msg_md *msg)
                bpf_printk("sk_msg2: length push_update %i->%i\n",
                           len2 ? len2 : len1, len3);
        }
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop) {
+               int err;
+
+               bpf_printk("sk_msg2: pop(%i@%i)\n",
+                          start_pop, pop);
+               err = bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+               if (err)
+                       bpf_printk("sk_msg2: pop_data err %i\n", err);
+               len4 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg2: length pop_data %i->%i\n",
+                          len1 ? len1 : 0,  len4);
+       }
 
        bpf_printk("sk_msg2: data length %i err1 %i err2 %i\n",
                   len1, err1, err2);
@@ -256,8 +274,8 @@ int bpf_prog5(struct sk_msg_md *msg)
 SEC("sk_msg3")
 int bpf_prog6(struct sk_msg_md *msg)
 {
-       int *bytes, *start, *end, *start_push, *end_push, *f;
-       int zero = 0, one = 1, two = 2, three = 3, key = 0;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5, key = 0;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop, *f;
        __u64 flags = 0;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
@@ -277,6 +295,11 @@ int bpf_prog6(struct sk_msg_md *msg)
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
 
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+
        f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
        if (f && *f) {
                key = 2;
@@ -292,8 +315,9 @@ int bpf_prog6(struct sk_msg_md *msg)
 SEC("sk_msg4")
 int bpf_prog7(struct sk_msg_md *msg)
 {
-       int zero = 0, one = 1, two = 2, three = 3, len1, len2 = 0, len3;
-       int *bytes, *start, *end, *start_push, *end_push, *f;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop, *f;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
+       int len1, len2 = 0, len3, len4;
        int err1 = 0, err2 = 0, key = 0;
        __u64 flags = 0;
 
@@ -335,6 +359,22 @@ int bpf_prog7(struct sk_msg_md *msg)
                           len2 ? len2 : len1, len3);
        }
 
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop) {
+               int err;
+
+               bpf_printk("sk_msg4: pop(%i@%i)\n",
+                          start_pop, pop);
+               err = bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+               if (err)
+                       bpf_printk("sk_msg4: pop_data err %i\n", err);
+               len4 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg4: length pop_data %i->%i\n",
+                          len1 ? len1 : 0,  len4);
+       }
+
+
        f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
        if (f && *f) {
                key = 2;
@@ -389,8 +429,8 @@ int bpf_prog9(struct sk_msg_md *msg)
 SEC("sk_msg7")
 int bpf_prog10(struct sk_msg_md *msg)
 {
-       int *bytes, *start, *end, *start_push, *end_push;
-       int zero = 0, one = 1, two = 2, three = 3;
+       int *bytes, *start, *end, *start_push, *end_push, *start_pop, *pop;
+       int zero = 0, one = 1, two = 2, three = 3, four = 4, five = 5;
 
        bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
        if (bytes)
@@ -406,7 +446,11 @@ int bpf_prog10(struct sk_msg_md *msg)
        end_push = bpf_map_lookup_elem(&sock_bytes, &three);
        if (start_push && end_push)
                bpf_msg_push_data(msg, *start_push, *end_push, 0);
-
+       start_pop = bpf_map_lookup_elem(&sock_bytes, &four);
+       pop = bpf_map_lookup_elem(&sock_bytes, &five);
+       if (start_pop && pop)
+               bpf_msg_pop_data(msg, *start_pop, *pop, 0);
+       bpf_printk("return sk drop\n");
        return SK_DROP;
 }
 
diff --git a/tools/testing/selftests/bpf/test_tcpnotify.h b/tools/testing/selftests/bpf/test_tcpnotify.h
new file mode 100644 (file)
index 0000000..8b6cea0
--- /dev/null
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef _TEST_TCPBPF_H
+#define _TEST_TCPBPF_H
+
+struct tcpnotify_globals {
+       __u32 total_retrans;
+       __u32 ncalls;
+};
+
+struct tcp_notifier {
+       __u8    type;
+       __u8    subtype;
+       __u8    source;
+       __u8    hash;
+};
+
+#define        TESTPORT        12877
+#endif
diff --git a/tools/testing/selftests/bpf/test_tcpnotify_kern.c b/tools/testing/selftests/bpf/test_tcpnotify_kern.c
new file mode 100644 (file)
index 0000000..edbca20
--- /dev/null
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/tcp.h>
+#include <netinet/in.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+#include "test_tcpnotify.h"
+
+struct bpf_map_def SEC("maps") global_map = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(__u32),
+       .value_size = sizeof(struct tcpnotify_globals),
+       .max_entries = 4,
+};
+
+struct bpf_map_def SEC("maps") perf_event_map = {
+       .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(__u32),
+       .max_entries = 2,
+};
+
+int _version SEC("version") = 1;
+
+SEC("sockops")
+int bpf_testcb(struct bpf_sock_ops *skops)
+{
+       int rv = -1;
+       int op;
+
+       op = (int) skops->op;
+
+       if (bpf_ntohl(skops->remote_port) != TESTPORT) {
+               skops->reply = -1;
+               return 0;
+       }
+
+       switch (op) {
+       case BPF_SOCK_OPS_TIMEOUT_INIT:
+       case BPF_SOCK_OPS_RWND_INIT:
+       case BPF_SOCK_OPS_NEEDS_ECN:
+       case BPF_SOCK_OPS_BASE_RTT:
+       case BPF_SOCK_OPS_RTO_CB:
+               rv = 1;
+               break;
+
+       case BPF_SOCK_OPS_TCP_CONNECT_CB:
+       case BPF_SOCK_OPS_TCP_LISTEN_CB:
+       case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
+       case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
+               bpf_sock_ops_cb_flags_set(skops, (BPF_SOCK_OPS_RETRANS_CB_FLAG|
+                                         BPF_SOCK_OPS_RTO_CB_FLAG));
+               rv = 1;
+               break;
+       case BPF_SOCK_OPS_RETRANS_CB: {
+                       __u32 key = 0;
+                       struct tcpnotify_globals g, *gp;
+                       struct tcp_notifier msg = {
+                               .type = 0xde,
+                               .subtype = 0xad,
+                               .source = 0xbe,
+                               .hash = 0xef,
+                       };
+
+                       rv = 1;
+
+                       /* Update results */
+                       gp = bpf_map_lookup_elem(&global_map, &key);
+                       if (!gp)
+                               break;
+                       g = *gp;
+                       g.total_retrans = skops->total_retrans;
+                       g.ncalls++;
+                       bpf_map_update_elem(&global_map, &key, &g,
+                                           BPF_ANY);
+                       bpf_perf_event_output(skops, &perf_event_map,
+                                             BPF_F_CURRENT_CPU,
+                                             &msg, sizeof(msg));
+               }
+               break;
+       default:
+               rv = -1;
+       }
+       skops->reply = rv;
+       return 1;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_tcpnotify_user.c b/tools/testing/selftests/bpf/test_tcpnotify_user.c
new file mode 100644 (file)
index 0000000..ff3c452
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <asm/types.h>
+#include <sys/syscall.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <sys/socket.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <sys/ioctl.h>
+#include <linux/rtnetlink.h>
+#include <signal.h>
+#include <linux/perf_event.h>
+
+#include "bpf_rlimit.h"
+#include "bpf_util.h"
+#include "cgroup_helpers.h"
+
+#include "test_tcpnotify.h"
+#include "trace_helpers.h"
+
+#define SOCKET_BUFFER_SIZE (getpagesize() < 8192L ? getpagesize() : 8192L)
+
+pthread_t tid;
+int rx_callbacks;
+
+static int dummyfn(void *data, int size)
+{
+       struct tcp_notifier *t = data;
+
+       if (t->type != 0xde || t->subtype != 0xad ||
+           t->source != 0xbe || t->hash != 0xef)
+               return 1;
+       rx_callbacks++;
+       return 0;
+}
+
+void tcp_notifier_poller(int fd)
+{
+       while (1)
+               perf_event_poller(fd, dummyfn);
+}
+
+static void *poller_thread(void *arg)
+{
+       int fd = *(int *)arg;
+
+       tcp_notifier_poller(fd);
+       return arg;
+}
+
+int verify_result(const struct tcpnotify_globals *result)
+{
+       return (result->ncalls > 0 && result->ncalls == rx_callbacks ? 0 : 1);
+}
+
+static int bpf_find_map(const char *test, struct bpf_object *obj,
+                       const char *name)
+{
+       struct bpf_map *map;
+
+       map = bpf_object__find_map_by_name(obj, name);
+       if (!map) {
+               printf("%s:FAIL:map '%s' not found\n", test, name);
+               return -1;
+       }
+       return bpf_map__fd(map);
+}
+
+static int setup_bpf_perf_event(int mapfd)
+{
+       struct perf_event_attr attr = {
+               .sample_type = PERF_SAMPLE_RAW,
+               .type = PERF_TYPE_SOFTWARE,
+               .config = PERF_COUNT_SW_BPF_OUTPUT,
+       };
+       int key = 0;
+       int pmu_fd;
+
+       pmu_fd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, 0);
+       if (pmu_fd < 0)
+               return pmu_fd;
+       bpf_map_update_elem(mapfd, &key, &pmu_fd, BPF_ANY);
+
+       ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+       return pmu_fd;
+}
+
+int main(int argc, char **argv)
+{
+       const char *file = "test_tcpnotify_kern.o";
+       int prog_fd, map_fd, perf_event_fd;
+       struct tcpnotify_globals g = {0};
+       const char *cg_path = "/foo";
+       int error = EXIT_FAILURE;
+       struct bpf_object *obj;
+       int cg_fd = -1;
+       __u32 key = 0;
+       int rv;
+       char test_script[80];
+       int pmu_fd;
+       cpu_set_t cpuset;
+
+       CPU_ZERO(&cpuset);
+       CPU_SET(0, &cpuset);
+       pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+
+       if (setup_cgroup_environment())
+               goto err;
+
+       cg_fd = create_and_get_cgroup(cg_path);
+       if (!cg_fd)
+               goto err;
+
+       if (join_cgroup(cg_path))
+               goto err;
+
+       if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
+               printf("FAILED: load_bpf_file failed for: %s\n", file);
+               goto err;
+       }
+
+       rv = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_SOCK_OPS, 0);
+       if (rv) {
+               printf("FAILED: bpf_prog_attach: %d (%s)\n",
+                      error, strerror(errno));
+               goto err;
+       }
+
+       perf_event_fd = bpf_find_map(__func__, obj, "perf_event_map");
+       if (perf_event_fd < 0)
+               goto err;
+
+       map_fd = bpf_find_map(__func__, obj, "global_map");
+       if (map_fd < 0)
+               goto err;
+
+       pmu_fd = setup_bpf_perf_event(perf_event_fd);
+       if (pmu_fd < 0 || perf_event_mmap(pmu_fd) < 0)
+               goto err;
+
+       pthread_create(&tid, NULL, poller_thread, (void *)&pmu_fd);
+
+       sprintf(test_script,
+               "/usr/sbin/iptables -A INPUT -p tcp --dport %d -j DROP",
+               TESTPORT);
+       system(test_script);
+
+       sprintf(test_script,
+               "/usr/bin/nc 127.0.0.1 %d < /etc/passwd > /dev/null 2>&1 ",
+               TESTPORT);
+       system(test_script);
+
+       sprintf(test_script,
+               "/usr/sbin/iptables -D INPUT -p tcp --dport %d -j DROP",
+               TESTPORT);
+       system(test_script);
+
+       rv = bpf_map_lookup_elem(map_fd, &key, &g);
+       if (rv != 0) {
+               printf("FAILED: bpf_map_lookup_elem returns %d\n", rv);
+               goto err;
+       }
+
+       sleep(10);
+
+       if (verify_result(&g)) {
+               printf("FAILED: Wrong stats Expected %d calls, got %d\n",
+                       g.ncalls, rx_callbacks);
+               goto err;
+       }
+
+       printf("PASSED!\n");
+       error = 0;
+err:
+       bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
+       close(cg_fd);
+       cleanup_cgroup_environment();
+       return error;
+}
index df6f751..1b7760d 100644 (file)
@@ -2026,29 +2026,27 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 1",
+               "check skb->hash byte load permitted 1",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, hash) + 1),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 2",
+               "check skb->hash byte load permitted 2",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
                        BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, hash) + 2),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
-               "check skb->hash byte load not permitted 3",
+               "check skb->hash byte load permitted 3",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -2060,8 +2058,7 @@ static struct bpf_test tests[] = {
 #endif
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid bpf_context access",
-               .result = REJECT,
+               .result = ACCEPT,
        },
        {
                "check cb access: byte, wrong type",
@@ -2173,7 +2170,7 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
        },
        {
-               "check skb->hash half load not permitted",
+               "check skb->hash half load permitted 2",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_0, 0),
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -2185,6 +2182,37 @@ static struct bpf_test tests[] = {
 #endif
                        BPF_EXIT_INSN(),
                },
+               .result = ACCEPT,
+       },
+       {
+               "check skb->hash half load not permitted, unaligned 1",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 1),
+#else
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 3),
+#endif
+                       BPF_EXIT_INSN(),
+               },
+               .errstr = "invalid bpf_context access",
+               .result = REJECT,
+       },
+       {
+               "check skb->hash half load not permitted, unaligned 3",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 3),
+#else
+                       BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, hash) + 1),
+#endif
+                       BPF_EXIT_INSN(),
+               },
                .errstr = "invalid bpf_context access",
                .result = REJECT,
        },
@@ -2418,6 +2446,10 @@ static struct bpf_test tests[] = {
                                    offsetof(struct __sk_buff, tc_index)),
                        BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
                                    offsetof(struct __sk_buff, cb[3])),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+                                   offsetof(struct __sk_buff, tstamp)),
                        BPF_EXIT_INSN(),
                },
                .errstr_unpriv = "",
@@ -5270,6 +5302,31 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
        },
        {
+               "write tstamp from CGROUP_SKB",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = REJECT,
+               .errstr_unpriv = "invalid bpf_context access off=152 size=8",
+               .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       },
+       {
+               "read tstamp from CGROUP_SKB",
+               .insns = {
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, tstamp)),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+       },
+       {
                "multiple registers share map_lookup_elem result",
                .insns = {
                        BPF_MOV64_IMM(BPF_REG_1, 10),
diff --git a/tools/testing/selftests/bpf/xdp_dummy.c b/tools/testing/selftests/bpf/xdp_dummy.c
new file mode 100644 (file)
index 0000000..43b0ef1
--- /dev/null
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define KBUILD_MODNAME "xdp_dummy"
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+SEC("xdp_dummy")
+int xdp_dummy_prog(struct xdp_md *ctx)
+{
+       return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/drivers/net/mlxsw/extack.sh b/tools/testing/selftests/drivers/net/mlxsw/extack.sh
new file mode 100755 (executable)
index 0000000..101a550
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test operations that we expect to report extended ack.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+       netdev_pre_up_test
+"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+
+setup_prepare()
+{
+       swp1=${NETIFS[p1]}
+       swp2=${NETIFS[p2]}
+
+       ip link set dev $swp1 up
+       ip link set dev $swp2 up
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ip link set dev $swp2 down
+       ip link set dev $swp1 down
+}
+
+netdev_pre_up_test()
+{
+       RET=0
+
+       ip link add name br1 up type bridge vlan_filtering 0 mcast_snooping 0
+       ip link add name vx1 up type vxlan id 1000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 nolearning noudpcsum tos inherit ttl 100
+
+       ip link set dev vx1 master br1
+       check_err $?
+
+       ip link set dev $swp1 master br1
+       check_err $?
+
+       ip link add name br2 up type bridge vlan_filtering 0 mcast_snooping 0
+       ip link add name vx2 up type vxlan id 2000 \
+               local 192.0.2.17 remote 192.0.2.18 \
+               dstport 4789 nolearning noudpcsum tos inherit ttl 100
+
+       ip link set dev vx2 master br2
+       check_err $?
+
+       ip link set dev $swp2 master br2
+       check_err $?
+
+       # Unsupported configuration: mlxsw demands that all offloaded VXLAN
+       # devices have the same TTL.
+       ip link set dev vx2 down
+       ip link set dev vx2 type vxlan ttl 200
+
+       ip link set dev vx2 up &>/dev/null
+       check_fail $?
+
+       ip link set dev vx2 up 2>&1 >/dev/null | grep -q mlxsw_spectrum
+       check_err $?
+
+       log_test "extack - NETDEV_PRE_UP"
+
+       ip link del dev vx2
+       ip link del dev br2
+
+       ip link del dev vx1
+       ip link del dev br1
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh b/tools/testing/selftests/drivers/net/mlxsw/one_armed_router.sh
new file mode 100755 (executable)
index 0000000..f02d83e
--- /dev/null
@@ -0,0 +1,259 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test a "one-armed router" [1] scenario. Packets forwarded between H1 and H2
+# should be forwarded by the ASIC, but also trapped so that ICMP redirect
+# packets could be potentially generated.
+#
+# 1. https://en.wikipedia.org/wiki/One-armed_router
+#
+# +---------------------------------+
+# | H1 (vrf)                        |
+# |    + $h1                        |
+# |    | 192.0.2.1/24               |
+# |    | 2001:db8:1::1/64           |
+# |    |                            |
+# |    |  default via 192.0.2.2     |
+# |    |  default via 2001:db8:1::2 |
+# +----|----------------------------+
+#      |
+# +----|----------------------------------------------------------------------+
+# | SW |                                                                      |
+# | +--|--------------------------------------------------------------------+ |
+# | |  + $swp1                   BR0 (802.1d)                               | |
+# | |                                                                       | |
+# | |                            192.0.2.2/24                               | |
+# | |                          2001:db8:1::2/64                             | |
+# | |                           198.51.100.2/24                             | |
+# | |                          2001:db8:2::2/64                             | |
+# | |                                                                       | |
+# | |  + $swp2                                                              | |
+# | +--|--------------------------------------------------------------------+ |
+# |    |                                                                      |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|----------------------------+
+# |    |  default via 198.51.100.2  |
+# |    |  default via 2001:db8:2::2 |
+# |    |                            |
+# |    | 2001:db8:2::1/64           |
+# |    | 198.51.100.1/24            |
+# |    + $h2                        |
+# | H2 (vrf)                        |
+# +---------------------------------+
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="ping_ipv4 ping_ipv6 fwd_mark_ipv4 fwd_mark_ipv6"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64
+
+       ip -4 route add default vrf v$h1 nexthop via 192.0.2.2
+       ip -6 route add default vrf v$h1 nexthop via 2001:db8:1::2
+}
+
+h1_destroy()
+{
+       ip -6 route del default vrf v$h1 nexthop via 2001:db8:1::2
+       ip -4 route del default vrf v$h1 nexthop via 192.0.2.2
+
+       simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64
+}
+
+h2_create()
+{
+       simple_if_init $h2 198.51.100.1/24 2001:db8:2::1/64
+
+       ip -4 route add default vrf v$h2 nexthop via 198.51.100.2
+       ip -6 route add default vrf v$h2 nexthop via 2001:db8:2::2
+}
+
+h2_destroy()
+{
+       ip -6 route del default vrf v$h2 nexthop via 2001:db8:2::2
+       ip -4 route del default vrf v$h2 nexthop via 198.51.100.2
+
+       simple_if_fini $h2 198.51.100.1/24 2001:db8:2::1/64
+}
+
+switch_create()
+{
+       ip link add name br0 type bridge mcast_snooping 0
+       ip link set dev br0 up
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp1 up
+       ip link set dev $swp2 master br0
+       ip link set dev $swp2 up
+
+       tc qdisc add dev $swp1 clsact
+       tc qdisc add dev $swp2 clsact
+
+       __addr_add_del br0 add 192.0.2.2/24 2001:db8:1::2/64
+       __addr_add_del br0 add 198.51.100.2/24 2001:db8:2::2/64
+}
+
+switch_destroy()
+{
+       __addr_add_del br0 del 198.51.100.2/24 2001:db8:2::2/64
+       __addr_add_del br0 del 192.0.2.2/24 2001:db8:1::2/64
+
+       tc qdisc del dev $swp2 clsact
+       tc qdisc del dev $swp1 clsact
+
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       ip link set dev br0 down
+       ip link del dev br0
+}
+
+ping_ipv4()
+{
+       ping_test $h1 198.51.100.1 ": h1->h2"
+}
+
+ping_ipv6()
+{
+       ping6_test $h1 2001:db8:2::1 ": h1->h2"
+}
+
+fwd_mark_ipv4()
+{
+       # Transmit packets from H1 to H2 and make sure they are trapped at
+       # swp1 due to loopback error, but only forwarded by the ASIC through
+       # swp2
+
+       tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
+               skip_hw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ip pref 1 handle 101 flower \
+               skip_hw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ip pref 2 handle 102 flower \
+               skip_sw dst_ip 198.51.100.1 ip_proto udp dst_port 52768 \
+               action pass
+
+       ip vrf exec v$h1 $MZ $h1 -c 10 -d 100msec -p 64 -A 192.0.2.1 \
+               -B 198.51.100.1 -t udp dp=52768,sp=42768 -q
+
+       RET=0
+
+       tc_check_packets "dev $swp1 ingress" 101 10
+       check_err $?
+
+       log_test "fwd mark: trapping IPv4 packets due to LBERROR"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 101 0
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv4 packets in software"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 102 10
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv4 packets in hardware"
+
+       tc filter del dev $swp2 egress protocol ip pref 2 handle 102 flower
+       tc filter del dev $swp2 egress protocol ip pref 1 handle 101 flower
+       tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
+}
+
+fwd_mark_ipv6()
+{
+       tc filter add dev $swp1 ingress protocol ipv6 pref 1 handle 101 flower \
+               skip_hw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ipv6 pref 1 handle 101 flower \
+               skip_hw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       tc filter add dev $swp2 egress protocol ipv6 pref 2 handle 102 flower \
+               skip_sw dst_ip 2001:db8:2::1 ip_proto udp dst_port 52768 \
+               action pass
+
+       ip vrf exec v$h1 $MZ $h1 -6 -c 10 -d 100msec -p 64 -A 2001:db8:1::1 \
+               -B 2001:db8:2::1 -t udp dp=52768,sp=42768 -q
+
+       RET=0
+
+       tc_check_packets "dev $swp1 ingress" 101 10
+       check_err $?
+
+       log_test "fwd mark: trapping IPv6 packets due to LBERROR"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 101 0
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv6 packets in software"
+
+       RET=0
+
+       tc_check_packets "dev $swp2 egress" 102 10
+       check_err $?
+
+       log_test "fwd mark: forwarding IPv6 packets in hardware"
+
+       tc filter del dev $swp2 egress protocol ipv6 pref 2 handle 102 flower
+       tc filter del dev $swp2 egress protocol ipv6 pref 1 handle 101 flower
+       tc filter del dev $swp1 ingress protocol ipv6 pref 1 handle 101 flower
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       vrf_prepare
+       forwarding_enable
+
+       sysctl_set net.ipv4.conf.all.accept_redirects 0
+       sysctl_set net.ipv6.conf.all.accept_redirects 0
+
+       h1_create
+       h2_create
+       switch_create
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       sysctl_restore net.ipv6.conf.all.accept_redirects
+       sysctl_restore net.ipv4.conf.all.accept_redirects
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
index 3b75180..00ae99f 100755 (executable)
@@ -8,7 +8,7 @@
 lib_dir=$(dirname $0)/../../../../net/forwarding
 
 ALL_TESTS="single_mask_test identical_filters_test two_masks_test \
-       multiple_masks_test ctcam_edge_cases_test"
+       multiple_masks_test ctcam_edge_cases_test delta_simple_test"
 NUM_NETIFS=2
 source $lib_dir/tc_common.sh
 source $lib_dir/lib.sh
@@ -142,7 +142,7 @@ two_masks_test()
        tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
                $tcflags dst_ip 192.0.2.2 action drop
        tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
-               $tcflags dst_ip 192.0.0.0/16 action drop
+               $tcflags dst_ip 192.0.0.0/8 action drop
 
        $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
                -t ip -q
@@ -235,7 +235,7 @@ ctcam_two_atcam_masks_test()
                $tcflags dst_ip 192.0.2.2 action drop
        # Filter goes into A-TCAM
        tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
-               $tcflags dst_ip 192.0.2.0/24 action drop
+               $tcflags dst_ip 192.0.0.0/16 action drop
 
        $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
                -t ip -q
@@ -324,6 +324,86 @@ ctcam_edge_cases_test()
        ctcam_no_atcam_masks_test
 }
 
+tp_record()
+{
+       local tracepoint=$1
+       local cmd=$2
+
+       perf record -q -e $tracepoint $cmd
+       return $?
+}
+
+tp_check_hits()
+{
+       local tracepoint=$1
+       local count=$2
+
+       perf_output=`perf script -F trace:event,trace`
+       hits=`echo $perf_output | grep "$tracepoint:" | wc -l`
+       if [[ "$count" -ne "$hits" ]]; then
+               return 1
+       fi
+       return 0
+}
+
+delta_simple_test()
+{
+       # The first filter will create eRP, the second filter will fit into
+       # the first eRP with delta. Remove the first rule then and check that
+        # the eRP stays (referenced by the second filter).
+
+       RET=0
+
+       if [[ "$tcflags" != "skip_sw" ]]; then
+               return 0;
+       fi
+
+       tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \
+                  pref 1 handle 101 flower $tcflags dst_ip 192.0.0.0/24 \
+                  action drop"
+       tp_check_hits "objagg:objagg_obj_root_create" 1
+       check_err $? "eRP was not created"
+
+       tp_record "objagg:*" "tc filter add dev $h2 ingress protocol ip \
+                  pref 2 handle 102 flower $tcflags dst_ip 192.0.2.2 \
+                  action drop"
+       tp_check_hits "objagg:objagg_obj_root_create" 0
+       check_err $? "eRP was incorrectly created"
+       tp_check_hits "objagg:objagg_obj_parent_assign" 1
+       check_err $? "delta was not created"
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 101 1
+       check_fail $? "Matched a wrong filter"
+
+       tc_check_packets "dev $h2 ingress" 102 1
+       check_err $? "Did not match on correct filter"
+
+       tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
+                  pref 1 handle 101 flower"
+       tp_check_hits "objagg:objagg_obj_root_destroy" 0
+       check_err $? "eRP was incorrectly destroyed"
+       tp_check_hits "objagg:objagg_obj_parent_unassign" 0
+       check_err $? "delta was incorrectly destroyed"
+
+       $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+               -t ip -q
+
+       tc_check_packets "dev $h2 ingress" 102 2
+       check_err $? "Did not match on correct filter after the first was removed"
+
+       tp_record "objagg:*" "tc filter del dev $h2 ingress protocol ip \
+                  pref 2 handle 102 flower"
+       tp_check_hits "objagg:objagg_obj_parent_unassign" 1
+       check_err $? "delta was not destroyed"
+       tp_check_hits "objagg:objagg_obj_root_destroy" 1
+       check_err $? "eRP was not destroyed"
+
+       log_test "delta simple test ($tcflags)"
+}
+
 setup_prepare()
 {
        h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/drivers/net/mlxsw/vxlan.sh b/tools/testing/selftests/drivers/net/mlxsw/vxlan.sh
new file mode 100755 (executable)
index 0000000..ea11535
--- /dev/null
@@ -0,0 +1,1043 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test various aspects of VxLAN offloading which are specific to mlxsw, such
+# as sanitization of invalid configurations and offload indication.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="sanitization_test offload_indication_test \
+       sanitization_vlan_aware_test offload_indication_vlan_aware_test"
+NUM_NETIFS=2
+source $lib_dir/lib.sh
+
+setup_prepare()
+{
+       swp1=${NETIFS[p1]}
+       swp2=${NETIFS[p2]}
+
+       ip link set dev $swp1 up
+       ip link set dev $swp2 up
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ip link set dev $swp2 down
+       ip link set dev $swp1 down
+}
+
+sanitization_single_dev_test_pass()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+
+       ip link set dev $swp1 nomaster
+
+       ip link set dev $swp1 master br0
+       check_err $?
+}
+
+sanitization_single_dev_test_fail()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0 &> /dev/null
+       check_fail $?
+
+       ip link set dev $swp1 nomaster
+
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp1 master br0 &> /dev/null
+       check_fail $?
+}
+
+sanitization_single_dev_valid_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device - valid configuration"
+}
+
+sanitization_single_dev_vlan_aware_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0 vlan_filtering 1
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a vlan-aware bridge"
+}
+
+sanitization_single_dev_mcast_enabled_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a multicast enabled bridge"
+}
+
+sanitization_single_dev_mcast_group_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 \
+               dev $swp2 group 239.0.0.1
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with a multicast group"
+}
+
+sanitization_single_dev_no_local_ip_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with no local ip"
+}
+
+sanitization_single_dev_local_ipv6_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 2001:db8::1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with local ipv6 address"
+}
+
+sanitization_single_dev_learning_enabled_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 learning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_pass
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with learning enabled"
+}
+
+sanitization_single_dev_local_interface_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 dev $swp2
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with local interface"
+}
+
+sanitization_single_dev_port_range_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789 \
+               srcport 4000 5000
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with udp source port range"
+}
+
+sanitization_single_dev_tos_static_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos 20 local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with static tos"
+}
+
+sanitization_single_dev_ttl_inherit_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl inherit tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with inherit ttl"
+}
+
+sanitization_single_dev_udp_checksum_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning udpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_single_dev_test_fail
+
+       ip link del dev vxlan0
+       ip link del dev br0
+
+       log_test "vxlan device with udp checksum"
+}
+
+sanitization_single_dev_test()
+{
+       # These tests make sure that we correctly sanitize VxLAN device
+       # configurations we do not support
+       sanitization_single_dev_valid_test
+       sanitization_single_dev_vlan_aware_test
+       sanitization_single_dev_mcast_enabled_test
+       sanitization_single_dev_mcast_group_test
+       sanitization_single_dev_no_local_ip_test
+       sanitization_single_dev_local_ipv6_test
+       sanitization_single_dev_learning_enabled_test
+       sanitization_single_dev_local_interface_test
+       sanitization_single_dev_port_range_test
+       sanitization_single_dev_tos_static_test
+       sanitization_single_dev_ttl_inherit_test
+       sanitization_single_dev_udp_checksum_test
+}
+
+sanitization_multi_devs_test_pass()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+       ip link set dev vxlan1 master br1
+       check_err $?
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+}
+
+sanitization_multi_devs_test_fail()
+{
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev vxlan0 master br0
+       check_err $?
+       ip link set dev $swp2 master br1
+       check_err $?
+       ip link set dev vxlan1 master br1 &> /dev/null
+       check_fail $?
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link set dev vxlan1 master br1
+       check_err $?
+       ip link set dev $swp1 master br0
+       check_err $?
+       ip link set dev $swp2 master br1 &> /dev/null
+       check_fail $?
+}
+
+sanitization_multi_devs_valid_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_multi_devs_test_pass
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices - valid configuration"
+}
+
+sanitization_multi_devs_ttl_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 40 tos inherit local 198.51.100.1 dstport 4789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different ttl"
+}
+
+sanitization_multi_devs_udp_dstport_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 5789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different udp destination port"
+}
+
+sanitization_multi_devs_local_ip_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link add dev br1 type bridge mcast_snooping 0
+
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up type vxlan id 20 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.2 dstport 4789
+
+       sanitization_multi_devs_test_fail
+
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+       ip link del dev br1
+       ip link del dev br0
+
+       log_test "multiple vxlan devices with different local ip"
+}
+
+sanitization_multi_devs_test()
+{
+       # The device has a single VTEP, which means all the VxLAN devices
+       # we offload must share certain properties such as source IP and
+       # UDP destination port. These tests make sure that we forbid
+       # configurations that violate this limitation
+       sanitization_multi_devs_valid_test
+       sanitization_multi_devs_ttl_test
+       sanitization_multi_devs_udp_dstport_test
+       sanitization_multi_devs_local_ip_test
+}
+
+sanitization_test()
+{
+       sanitization_single_dev_test
+       sanitization_multi_devs_test
+}
+
+offload_indication_setup_create()
+{
+       # Create a simple setup with two bridges, each with a VxLAN device
+       # and one local port
+       ip link add name br0 up type bridge mcast_snooping 0
+       ip link add name br1 up type bridge mcast_snooping 0
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link add name vxlan0 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up master br1 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+}
+
+offload_indication_setup_destroy()
+{
+       ip link del dev vxlan1
+       ip link del dev vxlan0
+
+       ip address del 198.51.100.1/32 dev lo
+
+       ip link set dev $swp2 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip link del dev br1
+       ip link del dev br0
+}
+
+offload_indication_fdb_flood_test()
+{
+       RET=0
+
+       bridge fdb append 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
+
+       bridge fdb show brport vxlan0 | grep 00:00:00:00:00:00 \
+               | grep -q offload
+       check_err $?
+
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self
+
+       log_test "vxlan flood entry offload indication"
+}
+
+offload_indication_fdb_bridge_test()
+{
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self master static \
+               dst 198.51.100.2
+
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - initial state"
+
+       # Remove FDB entry from the bridge driver and check that corresponding
+       # entry in the VxLAN driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 master
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from bridge"
+
+       # Add the FDB entry back to the bridge driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 master static
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to bridge"
+
+       # Remove FDB entry from the VxLAN driver and check that corresponding
+       # entry in the bridge driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from vxlan"
+
+       # Add the FDB entry back to the VxLAN driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self dst 198.51.100.2
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to vxlan"
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self master
+}
+
+offload_indication_fdb_test()
+{
+       offload_indication_fdb_flood_test
+       offload_indication_fdb_bridge_test
+}
+
+offload_indication_decap_route_test()
+{
+       RET=0
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan0 down
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan1 down
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - vxlan device down"
+
+       RET=0
+
+       ip link set dev vxlan1 up
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev vxlan0 up
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - vxlan device up"
+
+       RET=0
+
+       ip address delete 198.51.100.1/32 dev lo
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       ip address add 198.51.100.1/32 dev lo
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - add local route"
+
+       RET=0
+
+       ip link set dev $swp1 nomaster
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link set dev $swp2 nomaster
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - local ports enslavement"
+
+       RET=0
+
+       ip link del dev br0
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - bridge device deletion"
+
+       RET=0
+
+       ip link add name br0 up type bridge mcast_snooping 0
+       ip link add name br1 up type bridge mcast_snooping 0
+       ip link set dev $swp1 master br0
+       ip link set dev $swp2 master br1
+       ip link set dev vxlan0 master br0
+       ip link set dev vxlan1 master br1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev vxlan0
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       ip link del dev vxlan1
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       log_test "vxlan decap route - vxlan device deletion"
+
+       ip link add name vxlan0 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan1 up master br1 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+}
+
+check_fdb_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
+       check_err $?
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_err $?
+}
+
+check_vxlan_fdb_not_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
+       check_fail $?
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep -q self
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_fail $?
+}
+
+check_bridge_fdb_not_offloaded()
+{
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb show dev vxlan0 | grep $mac | grep -q master
+       check_err $?
+       bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
+       check_fail $?
+}
+
+__offload_indication_join_vxlan_first()
+{
+       local vid=$1; shift
+
+       local mac=00:11:22:33:44:55
+       local zmac=00:00:00:00:00:00
+
+       bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
+
+       ip link set dev vxlan0 master br0
+       bridge fdb add dev vxlan0 $mac self master static dst 198.51.100.2
+
+       RET=0
+       check_vxlan_fdb_not_offloaded
+       ip link set dev $swp1 master br0
+       sleep .1
+       check_fdb_offloaded
+       log_test "offload indication - attach vxlan first"
+
+       RET=0
+       ip link set dev vxlan0 down
+       check_vxlan_fdb_not_offloaded
+       check_bridge_fdb_not_offloaded
+       log_test "offload indication - set vxlan down"
+
+       RET=0
+       ip link set dev vxlan0 up
+       sleep .1
+       check_fdb_offloaded
+       log_test "offload indication - set vxlan up"
+
+       if [[ ! -z $vid ]]; then
+               RET=0
+               bridge vlan del dev vxlan0 vid $vid
+               check_vxlan_fdb_not_offloaded
+               check_bridge_fdb_not_offloaded
+               log_test "offload indication - delete VLAN"
+
+               RET=0
+               bridge vlan add dev vxlan0 vid $vid
+               check_vxlan_fdb_not_offloaded
+               check_bridge_fdb_not_offloaded
+               log_test "offload indication - add tagged VLAN"
+
+               RET=0
+               bridge vlan add dev vxlan0 vid $vid pvid untagged
+               sleep .1
+               check_fdb_offloaded
+               log_test "offload indication - add pvid/untagged VLAN"
+       fi
+
+       RET=0
+       ip link set dev $swp1 nomaster
+       check_vxlan_fdb_not_offloaded
+       log_test "offload indication - detach port"
+}
+
+offload_indication_join_vxlan_first()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_first
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+__offload_indication_join_vxlan_last()
+{
+       local zmac=00:00:00:00:00:00
+
+       RET=0
+
+       bridge fdb append $zmac dev vxlan0 self dst 198.51.100.2
+
+       ip link set dev $swp1 master br0
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_fail $?
+
+       ip link set dev vxlan0 master br0
+
+       bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
+       check_err $?
+
+       log_test "offload indication - attach vxlan last"
+}
+
+offload_indication_join_vxlan_last()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_last
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_test()
+{
+       offload_indication_setup_create
+       offload_indication_fdb_test
+       offload_indication_decap_route_test
+       offload_indication_setup_destroy
+
+       log_info "offload indication - replay & cleanup"
+       offload_indication_join_vxlan_first
+       offload_indication_join_vxlan_last
+}
+
+sanitization_vlan_aware_test()
+{
+       RET=0
+
+       ip link add dev br0 type bridge mcast_snooping 0 vlan_filtering 1
+
+       ip link add name vxlan10 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       ip link add name vxlan20 up master br0 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       # Test that when each VNI is mapped to a different VLAN we can enslave
+       # a port to the bridge
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+
+       ip link set dev $swp1 master br0
+       check_err $?
+
+       log_test "vlan-aware - enslavement to vlan-aware bridge"
+
+       # Try to map both VNIs to the same VLAN and make sure configuration
+       # fails
+       RET=0
+
+       bridge vlan add vid 10 dev vxlan20 pvid untagged &> /dev/null
+       check_fail $?
+
+       log_test "vlan-aware - two vnis mapped to the same vlan"
+
+       # Test that enslavement of a port to a bridge fails when two VNIs
+       # are mapped to the same VLAN
+       RET=0
+
+       ip link set dev $swp1 nomaster
+
+       bridge vlan del vid 20 dev vxlan20 pvid untagged
+       bridge vlan add vid 10 dev vxlan20 pvid untagged
+
+       ip link set dev $swp1 master br0 &> /dev/null
+       check_fail $?
+
+       log_test "vlan-aware - failed enslavement to vlan-aware bridge"
+
+       ip link del dev vxlan20
+       ip link del dev vxlan10
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_setup_create()
+{
+       # Create a simple setup with two VxLAN devices and a single VLAN-aware
+       # bridge
+       ip link add name br0 up type bridge mcast_snooping 0 vlan_filtering 1 \
+               vlan_default_pvid 0
+
+       ip link set dev $swp1 master br0
+
+       bridge vlan add vid 10 dev $swp1
+       bridge vlan add vid 20 dev $swp1
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link add name vxlan10 up master br0 type vxlan id 10 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+       ip link add name vxlan20 up master br0 type vxlan id 20 nolearning \
+               noudpcsum ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+}
+
+offload_indication_vlan_aware_setup_destroy()
+{
+       bridge vlan del vid 20 dev vxlan20
+       bridge vlan del vid 10 dev vxlan10
+
+       ip link del dev vxlan20
+       ip link del dev vxlan10
+
+       ip address del 198.51.100.1/32 dev lo
+
+       bridge vlan del vid 20 dev $swp1
+       bridge vlan del vid 10 dev $swp1
+
+       ip link set dev $swp1 nomaster
+
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_fdb_test()
+{
+       RET=0
+
+       log_info "vxlan entry offload indication - vlan-aware"
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self master static \
+               dst 198.51.100.2 vlan 10
+
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - initial state"
+
+       # Remove FDB entry from the bridge driver and check that corresponding
+       # entry in the VxLAN driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 master vlan 10
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from bridge"
+
+       # Add the FDB entry back to the bridge driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 master static vlan 10
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to bridge"
+
+       # Remove FDB entry from the VxLAN driver and check that corresponding
+       # entry in the bridge driver is not marked as offloaded
+       RET=0
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_fail $?
+
+       log_test "vxlan entry offload indication - after removal from vxlan"
+
+       # Add the FDB entry back to the VxLAN driver and make sure it is
+       # marked as offloaded in both drivers
+       RET=0
+
+       bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self dst 198.51.100.2
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
+               | grep -q offload
+       check_err $?
+       bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
+               | grep -q offload
+       check_err $?
+
+       log_test "vxlan entry offload indication - after re-add to vxlan"
+
+       bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self master vlan 10
+}
+
+offload_indication_vlan_aware_decap_route_test()
+{
+       RET=0
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       # Toggle PVID flag on one VxLAN device and make sure route is still
+       # marked as offloaded
+       bridge vlan add vid 10 dev vxlan10 untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       # Toggle PVID flag on second VxLAN device and make sure route is no
+       # longer marked as offloaded
+       bridge vlan add vid 20 dev vxlan20 untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_fail $?
+
+       # Toggle PVID flag back and make sure route is marked as offloaded
+       bridge vlan add vid 10 dev vxlan10 pvid untagged
+       bridge vlan add vid 20 dev vxlan20 pvid untagged
+
+       ip route show table local | grep 198.51.100.1 | grep -q offload
+       check_err $?
+
+       log_test "vxlan decap route - vni map/unmap"
+}
+
+offload_indication_vlan_aware_join_vxlan_first()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0 \
+               vlan_filtering 1 vlan_default_pvid 1
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_first 1
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_join_vxlan_last()
+{
+       ip link add dev br0 up type bridge mcast_snooping 0 \
+               vlan_filtering 1 vlan_default_pvid 1
+       ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       __offload_indication_join_vxlan_last
+
+       ip link del dev vxlan0
+       ip link del dev br0
+}
+
+offload_indication_vlan_aware_test()
+{
+       offload_indication_vlan_aware_setup_create
+       offload_indication_vlan_aware_fdb_test
+       offload_indication_vlan_aware_decap_route_test
+       offload_indication_vlan_aware_setup_destroy
+
+       log_info "offload indication - replay & cleanup - vlan aware"
+       offload_indication_vlan_aware_join_vxlan_first
+       offload_indication_vlan_aware_join_vxlan_last
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh b/tools/testing/selftests/drivers/net/mlxsw/vxlan_flooding.sh
new file mode 100755 (executable)
index 0000000..fedcb7b
--- /dev/null
@@ -0,0 +1,309 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test VxLAN flooding. The device stores flood records in a singly linked list
+# where each record stores up to three IPv4 addresses of remote VTEPs. The test
+# verifies that packets are correctly flooded in various cases such as deletion
+# of a record in the middle of the list.
+#
+# +--------------------+
+# | H1 (vrf)           |
+# |    + $h1           |
+# |    | 203.0.113.1/24|
+# +----|---------------+
+#      |
+# +----|----------------------------------------------------------------------+
+# | SW |                                                                      |
+# | +--|--------------------------------------------------------------------+ |
+# | |  + $swp1                   BR0 (802.1d)                               | |
+# | |                                                                       | |
+# | |  + vxlan0 (vxlan)                                                     | |
+# | |    local 198.51.100.1                                                 | |
+# | |    remote 198.51.100.{2..13}                                          | |
+# | |    id 10 dstport 4789                                                 | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  198.51.100.0/24 via 192.0.2.2                                            |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.1/24                                                         |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                               R2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.2/24                                           |
+# |                                                             |
+# +-------------------------------------------------------------+
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="flooding_test"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 203.0.113.1/24
+}
+
+h1_destroy()
+{
+       simple_if_fini $h1 203.0.113.1/24
+}
+
+switch_create()
+{
+       # Make sure the bridge uses the MAC address of the local port and
+       # not that of the VxLAN's device
+       ip link add dev br0 type bridge mcast_snooping 0
+       ip link set dev br0 address $(mac_get $swp1)
+
+       ip link add name vxlan0 type vxlan id 10 nolearning noudpcsum \
+               ttl 20 tos inherit local 198.51.100.1 dstport 4789
+
+       ip address add 198.51.100.1/32 dev lo
+
+       ip link set dev $swp1 master br0
+       ip link set dev vxlan0 master br0
+
+       ip link set dev br0 up
+       ip link set dev $swp1 up
+       ip link set dev vxlan0 up
+}
+
+switch_destroy()
+{
+       ip link set dev vxlan0 down
+       ip link set dev $swp1 down
+       ip link set dev br0 down
+
+       ip link set dev vxlan0 nomaster
+       ip link set dev $swp1 nomaster
+
+       ip address del 198.51.100.1/32 dev lo
+
+       ip link del dev vxlan0
+
+       ip link del dev br0
+}
+
+router1_create()
+{
+       # This router is in the default VRF, where the VxLAN device is
+       # performing the L3 lookup
+       ip link set dev $rp1 up
+       ip address add 192.0.2.1/24 dev $rp1
+       ip route add 198.51.100.0/24 via 192.0.2.2
+}
+
+router1_destroy()
+{
+       ip route del 198.51.100.0/24 via 192.0.2.2
+       ip address del 192.0.2.1/24 dev $rp1
+       ip link set dev $rp1 down
+}
+
+router2_create()
+{
+       # This router is not in the default VRF, so use simple_if_init()
+       simple_if_init $rp2 192.0.2.2/24
+}
+
+router2_destroy()
+{
+       simple_if_fini $rp2 192.0.2.2/24
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       rp1=${NETIFS[p3]}
+       rp2=${NETIFS[p4]}
+
+       vrf_prepare
+
+       h1_create
+
+       switch_create
+
+       router1_create
+       router2_create
+
+       forwarding_enable
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       forwarding_restore
+
+       router2_destroy
+       router1_destroy
+
+       switch_destroy
+
+       h1_destroy
+
+       vrf_cleanup
+}
+
+flooding_remotes_add()
+{
+       local num_remotes=$1
+       local lsb
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               lsb=$((i + 1))
+
+               bridge fdb append 00:00:00:00:00:00 dev vxlan0 self \
+                       dst 198.51.100.$lsb
+       done
+}
+
+flooding_filters_add()
+{
+       local num_remotes=$1
+       local lsb
+       local i
+
+       tc qdisc add dev $rp2 clsact
+
+       for i in $(eval echo {1..$num_remotes}); do
+               lsb=$((i + 1))
+
+               tc filter add dev $rp2 ingress protocol ip pref $i handle $i \
+                       flower ip_proto udp dst_ip 198.51.100.$lsb \
+                       dst_port 4789 skip_sw action drop
+       done
+}
+
+flooding_filters_del()
+{
+       local num_remotes=$1
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               tc filter del dev $rp2 ingress protocol ip pref $i \
+                       handle $i flower
+       done
+
+       tc qdisc del dev $rp2 clsact
+}
+
+flooding_check_packets()
+{
+       local packets=("$@")
+       local num_remotes=${#packets[@]}
+       local i
+
+       for i in $(eval echo {1..$num_remotes}); do
+               tc_check_packets "dev $rp2 ingress" $i ${packets[i - 1]}
+               check_err $? "remote $i - did not get expected number of packets"
+       done
+}
+
+flooding_test()
+{
+       # Use 12 remote VTEPs that will be stored in 4 records. The array
+       # 'packets' will store how many packets are expected to be received
+       # by each remote VTEP at each stage of the test
+       declare -a packets=(1 1 1 1 1 1 1 1 1 1 1 1)
+       local num_remotes=12
+
+       RET=0
+
+       # Add FDB entries for remote VTEPs and corresponding tc filters on the
+       # ingress of the nexthop router. These filters will count how many
+       # packets were flooded to each remote VTEP
+       flooding_remotes_add $num_remotes
+       flooding_filters_add $num_remotes
+
+       # Send one packet and make sure it is flooded to all the remote VTEPs
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 1 packet"
+
+       # Delete the third record which corresponds to VTEPs with LSB 8..10
+       # and check that packet is flooded correctly when we remove a record
+       # from the middle of the list
+       RET=0
+
+       packets=(2 2 2 2 2 2 1 1 1 2 2 2)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.8
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.9
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.10
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 2 packets"
+
+       # Delete the first record and make sure the packet is flooded correctly
+       RET=0
+
+       packets=(2 2 2 3 3 3 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.3
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.4
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 3 packets"
+
+       # Delete the last record and make sure the packet is flooded correctly
+       RET=0
+
+       packets=(2 2 2 4 4 4 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.11
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.12
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.13
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 4 packets"
+
+       # Delete the last record, one entry at a time and make sure single
+       # entries are correctly removed
+       RET=0
+
+       packets=(2 2 2 4 5 5 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.5
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 5 packets"
+
+       RET=0
+
+       packets=(2 2 2 4 5 6 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.6
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 6 packets"
+
+       RET=0
+
+       packets=(2 2 2 4 5 6 1 1 1 3 3 3)
+       bridge fdb del 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.7
+
+       $MZ $h1 -q -p 64 -b de:ad:be:ef:13:37 -t ip -c 1
+       flooding_check_packets "${packets[@]}"
+       log_test "flood after 7 packets"
+
+       flooding_filters_del $num_remotes
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
index 8cf22b3..7f57b91 100644 (file)
@@ -14,4 +14,5 @@ udpgso_bench_rx
 udpgso_bench_tx
 tcp_inq
 tls
+txring_overwrite
 ip_defrag
index 256d82d..ee2e27b 100644 (file)
@@ -7,10 +7,11 @@ CFLAGS += -I../../../../usr/include/
 TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh
 TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh
 TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh
+TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
-TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd
+TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd txring_overwrite
 TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag
 TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
 TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
index cd3a2f1..5821bdd 100644 (file)
@@ -14,3 +14,17 @@ CONFIG_IPV6_VTI=y
 CONFIG_DUMMY=y
 CONFIG_BRIDGE=y
 CONFIG_VLAN_8021Q=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_NAT_IPV6=m
+CONFIG_NF_NAT_IPV4=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NFT_CHAIN_NAT_IPV4=m
index 85d2535..7af5a03 100644 (file)
@@ -104,7 +104,7 @@ create_netif_veth()
 {
        local i
 
-       for i in $(eval echo {1..$NUM_NETIFS}); do
+       for ((i = 1; i <= NUM_NETIFS; ++i)); do
                local j=$((i+1))
 
                ip link show dev ${NETIFS[p$i]} &> /dev/null
@@ -135,7 +135,7 @@ if [[ "$NETIF_CREATE" = "yes" ]]; then
        create_netif
 fi
 
-for i in $(eval echo {1..$NUM_NETIFS}); do
+for ((i = 1; i <= NUM_NETIFS; ++i)); do
        ip link show dev ${NETIFS[p$i]} &> /dev/null
        if [[ $? -ne 0 ]]; then
                echo "SKIP: could not find all required interfaces"
@@ -477,11 +477,24 @@ master_name_get()
        ip -j link show dev $if_name | jq -r '.[]["master"]'
 }
 
+link_stats_get()
+{
+       local if_name=$1; shift
+       local dir=$1; shift
+       local stat=$1; shift
+
+       ip -j -s link show dev $if_name \
+               | jq '.[]["stats64"]["'$dir'"]["'$stat'"]'
+}
+
 link_stats_tx_packets_get()
 {
-       local if_name=$1
+       link_stats_get $1 tx packets
+}
 
-       ip -j -s link show dev $if_name | jq '.[]["stats64"]["tx"]["packets"]'
+link_stats_rx_errors_get()
+{
+       link_stats_get $1 rx errors
 }
 
 tc_rule_stats_get()
@@ -783,6 +796,17 @@ multipath_eval()
        log_info "Expected ratio $weights_ratio Measured ratio $packets_ratio"
 }
 
+in_ns()
+{
+       local name=$1; shift
+
+       ip netns exec $name bash <<-EOF
+               NUM_NETIFS=0
+               source lib.sh
+               $(for a in "$@"; do printf "%q${IFS:0:1}" "$a"; done)
+       EOF
+}
+
 ##############################################################################
 # Tests
 
@@ -790,10 +814,11 @@ ping_do()
 {
        local if_name=$1
        local dip=$2
+       local args=$3
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name $PING $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
 }
 
 ping_test()
@@ -802,17 +827,18 @@ ping_test()
 
        ping_do $1 $2
        check_err $?
-       log_test "ping"
+       log_test "ping$3"
 }
 
 ping6_do()
 {
        local if_name=$1
        local dip=$2
+       local args=$3
        local vrf_name
 
        vrf_name=$(master_name_get $if_name)
-       ip vrf exec $vrf_name $PING6 $dip -c 10 -i 0.1 -w 2 &> /dev/null
+       ip vrf exec $vrf_name $PING6 $args $dip -c 10 -i 0.1 -w 2 &> /dev/null
 }
 
 ping6_test()
@@ -821,7 +847,7 @@ ping6_test()
 
        ping6_do $1 $2
        check_err $?
-       log_test "ping6"
+       log_test "ping6$3"
 }
 
 learning_test()
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1d.sh
new file mode 100755 (executable)
index 0000000..56cef3b
--- /dev/null
@@ -0,0 +1,786 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +--------------------+                               +----------------------+
+# | H1 (vrf)           |                               |             H2 (vrf) |
+# |    + $h1           |                               |  + $h2               |
+# |    | 192.0.2.1/28  |                               |  | 192.0.2.2/28      |
+# +----|---------------+                               +--|-------------------+
+#      |                                                  |
+# +----|--------------------------------------------------|-------------------+
+# | SW |                                                  |                   |
+# | +--|--------------------------------------------------|-----------------+ |
+# | |  + $swp1                   BR1 (802.1d)             + $swp2           | |
+# | |                                                                       | |
+# | |  + vx1 (vxlan)                                                        | |
+# | |    local 192.0.2.17                                                   | |
+# | |    remote 192.0.2.34 192.0.2.50                                       | |
+# | |    id 1000 dstport $VXPORT                                            | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  192.0.2.32/28 via 192.0.2.18                                             |
+# |  192.0.2.48/28 via 192.0.2.18                                             |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.17/28                                                        |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                             VRP2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.18/28                                          |
+# |                                                             |   (maybe) HW
+# =============================================================================
+# |                                                             |  (likely) SW
+# |    + v1 (veth)                             + v3 (veth)      |
+# |    | 192.0.2.33/28                         | 192.0.2.49/28  |
+# +----|---------------------------------------|----------------+
+#      |                                       |
+# +----|------------------------------+   +----|------------------------------+
+# |    + v2 (veth)        NS1 (netns) |   |    + v4 (veth)        NS2 (netns) |
+# |      192.0.2.34/28                |   |      192.0.2.50/28                |
+# |                                   |   |                                   |
+# |   192.0.2.16/28 via 192.0.2.33    |   |   192.0.2.16/28 via 192.0.2.49    |
+# |   192.0.2.50/32 via 192.0.2.33    |   |   192.0.2.34/32 via 192.0.2.49    |
+# |                                   |   |                                   |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# | |                  BR2 (802.1d) | |   | |                  BR2 (802.1d) | |
+# | |  + vx2 (vxlan)                | |   | |  + vx2 (vxlan)                | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 1000 dstport $VXPORT    | |   | |    id 1000 dstport $VXPORT    | |
+# | |                               | |   | |                               | |
+# | |  + w1 (veth)                  | |   | |  + w1 (veth)                  | |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# |    |                              |   |    |                              |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# | |  |                  VW2 (vrf) | |   | |  |                  VW2 (vrf) | |
+# | |  + w2 (veth)                  | |   | |  + w2 (veth)                  | |
+# | |    192.0.2.3/28               | |   | |    192.0.2.4/28               | |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# +-----------------------------------+   +-----------------------------------+
+
+: ${VXPORT:=4789}
+export VXPORT
+
+: ${ALL_TESTS:="
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_ttl
+       test_tos
+       test_ecn_encap
+       test_ecn_decap
+       reapply_config
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_learning
+    "}
+
+NUM_NETIFS=6
+source lib.sh
+
+h1_create()
+{
+       simple_if_init $h1 192.0.2.1/28
+       tc qdisc add dev $h1 clsact
+}
+
+h1_destroy()
+{
+       tc qdisc del dev $h1 clsact
+       simple_if_fini $h1 192.0.2.1/28
+}
+
+h2_create()
+{
+       simple_if_init $h2 192.0.2.2/28
+       tc qdisc add dev $h2 clsact
+}
+
+h2_destroy()
+{
+       tc qdisc del dev $h2 clsact
+       simple_if_fini $h2 192.0.2.2/28
+}
+
+rp1_set_addr()
+{
+       ip address add dev $rp1 192.0.2.17/28
+
+       ip route add 192.0.2.32/28 nexthop via 192.0.2.18
+       ip route add 192.0.2.48/28 nexthop via 192.0.2.18
+}
+
+rp1_unset_addr()
+{
+       ip route del 192.0.2.48/28 nexthop via 192.0.2.18
+       ip route del 192.0.2.32/28 nexthop via 192.0.2.18
+
+       ip address del dev $rp1 192.0.2.17/28
+}
+
+switch_create()
+{
+       ip link add name br1 type bridge vlan_filtering 0 mcast_snooping 0
+       # Make sure the bridge uses the MAC address of the local port and not
+       # that of the VxLAN's device.
+       ip link set dev br1 address $(mac_get $swp1)
+       ip link set dev br1 up
+
+       ip link set dev $rp1 up
+       rp1_set_addr
+
+       ip link add name vx1 type vxlan id 1000         \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx1 up
+
+       ip link set dev vx1 master br1
+       ip link set dev $swp1 master br1
+       ip link set dev $swp1 up
+
+       ip link set dev $swp2 master br1
+       ip link set dev $swp2 up
+
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+}
+
+switch_destroy()
+{
+       rp1_unset_addr
+       ip link set dev $rp1 down
+
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       ip link set dev vx1 nomaster
+       ip link set dev vx1 down
+       ip link del dev vx1
+
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       ip link set dev br1 down
+       ip link del dev br1
+}
+
+vrp2_create()
+{
+       simple_if_init $rp2 192.0.2.18/28
+       __simple_if_init v1 v$rp2 192.0.2.33/28
+       __simple_if_init v3 v$rp2 192.0.2.49/28
+       tc qdisc add dev v1 clsact
+}
+
+vrp2_destroy()
+{
+       tc qdisc del dev v1 clsact
+       __simple_if_fini v3 192.0.2.49/28
+       __simple_if_fini v1 192.0.2.33/28
+       simple_if_fini $rp2 192.0.2.18/28
+}
+
+ns_init_common()
+{
+       local in_if=$1; shift
+       local in_addr=$1; shift
+       local other_in_addr=$1; shift
+       local nh_addr=$1; shift
+       local host_addr=$1; shift
+
+       ip link set dev $in_if up
+       ip address add dev $in_if $in_addr/28
+       tc qdisc add dev $in_if clsact
+
+       ip link add name br2 type bridge vlan_filtering 0
+       ip link set dev br2 up
+
+       ip link add name w1 type veth peer name w2
+
+       ip link set dev w1 master br2
+       ip link set dev w1 up
+
+       ip link add name vx2 type vxlan id 1000 local $in_addr dstport "$VXPORT"
+       ip link set dev vx2 up
+       bridge fdb append dev vx2 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx2 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx2 master br2
+       tc qdisc add dev vx2 clsact
+
+       simple_if_init w2 $host_addr/28
+
+       ip route add 192.0.2.16/28 nexthop via $nh_addr
+       ip route add $other_in_addr/32 nexthop via $nh_addr
+}
+export -f ns_init_common
+
+ns1_create()
+{
+       ip netns add ns1
+       ip link set dev v2 netns ns1
+       in_ns ns1 \
+             ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3
+}
+
+ns1_destroy()
+{
+       ip netns exec ns1 ip link set dev v2 netns 1
+       ip netns del ns1
+}
+
+ns2_create()
+{
+       ip netns add ns2
+       ip link set dev v4 netns ns2
+       in_ns ns2 \
+             ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4
+}
+
+ns2_destroy()
+{
+       ip netns exec ns2 ip link set dev v4 netns 1
+       ip netns del ns2
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       rp1=${NETIFS[p5]}
+       rp2=${NETIFS[p6]}
+
+       vrf_prepare
+       forwarding_enable
+
+       h1_create
+       h2_create
+       switch_create
+
+       ip link add name v1 type veth peer name v2
+       ip link add name v3 type veth peer name v4
+       vrp2_create
+       ns1_create
+       ns2_create
+
+       r1_mac=$(in_ns ns1 mac_get w2)
+       r2_mac=$(in_ns ns2 mac_get w2)
+       h2_mac=$(mac_get $h2)
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ns2_destroy
+       ns1_destroy
+       vrp2_destroy
+       ip link del dev v3
+       ip link del dev v1
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+# For the first round of tests, vx1 is the first device to get attached to the
+# bridge, and that at the point that the local IP is already configured. Try the
+# other scenario of attaching the device to an already-offloaded bridge, and
+# only then attach the local IP.
+reapply_config()
+{
+       echo "Reapplying configuration"
+
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       rp1_unset_addr
+       ip link set dev vx1 nomaster
+       sleep 5
+
+       ip link set dev vx1 master br1
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx1 00:00:00:00:00:00 dst 192.0.2.50 self
+       sleep 1
+       rp1_set_addr
+       sleep 5
+}
+
+ping_ipv4()
+{
+       ping_test $h1 192.0.2.2 ": local->local"
+       ping_test $h1 192.0.2.3 ": local->remote 1"
+       ping_test $h1 192.0.2.4 ": local->remote 2"
+}
+
+maybe_in_ns()
+{
+       echo ${1:+in_ns} $1
+}
+
+__flood_counter_add_del()
+{
+       local add_del=$1; shift
+       local dev=$1; shift
+       local ns=$1; shift
+
+       # Putting the ICMP capture both to HW and to SW will end up
+       # double-counting the packets that are trapped to slow path, such as for
+       # the unicast test. Adding either skip_hw or skip_sw fixes this problem,
+       # but with skip_hw, the flooded packets are not counted at all, because
+       # those are dropped due to MAC address mismatch; and skip_sw is a no-go
+       # for veth-based topologies.
+       #
+       # So try to install with skip_sw and fall back to skip_sw if that fails.
+
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_sw 2>/dev/null || \
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_hw
+}
+
+flood_counter_install()
+{
+       __flood_counter_add_del add "$@"
+}
+
+flood_counter_uninstall()
+{
+       __flood_counter_add_del del "$@"
+}
+
+flood_fetch_stat()
+{
+       local dev=$1; shift
+       local ns=$1; shift
+
+       $(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
+}
+
+flood_fetch_stats()
+{
+       local counters=("${@}")
+       local counter
+
+       for counter in "${counters[@]}"; do
+               flood_fetch_stat $counter
+       done
+}
+
+vxlan_flood_test()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local -a expects=("${@}")
+
+       local -a counters=($h2 "vx2 ns1" "vx2 ns2")
+       local counter
+       local key
+
+       for counter in "${counters[@]}"; do
+               flood_counter_install $counter
+       done
+
+       local -a t0s=($(flood_fetch_stats "${counters[@]}"))
+       $MZ $h1 -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
+       sleep 1
+       local -a t1s=($(flood_fetch_stats "${counters[@]}"))
+
+       for key in ${!t0s[@]}; do
+               local delta=$((t1s[$key] - t0s[$key]))
+               local expect=${expects[$key]}
+
+               ((expect == delta))
+               check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
+       done
+
+       for counter in "${counters[@]}"; do
+               flood_counter_uninstall $counter
+       done
+}
+
+__test_flood()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: $what"
+}
+
+test_flood()
+{
+       __test_flood de:ad:be:ef:13:37 192.0.2.100 "flood"
+}
+
+vxlan_fdb_add_del()
+{
+       local add_del=$1; shift
+       local mac=$1; shift
+       local dev=$1; shift
+       local dst=$1; shift
+
+       bridge fdb $add_del dev $dev $mac self static permanent \
+               ${dst:+dst} $dst 2>/dev/null
+       bridge fdb $add_del dev $dev $mac master static 2>/dev/null
+}
+
+__test_unicast()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local hit_idx=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       local -a expects=(0 0 0)
+       expects[$hit_idx]=10
+
+       vxlan_flood_test $mac $dst "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_unicast()
+{
+       local -a targets=("$h2_mac $h2"
+                         "$r1_mac vx1 192.0.2.34"
+                         "$r2_mac vx1 192.0.2.50")
+       local target
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add $target
+       done
+
+       __test_unicast $h2_mac 192.0.2.2 0 "local MAC unicast"
+       __test_unicast $r1_mac 192.0.2.3 1 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 192.0.2.4 2 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del $target
+       done
+}
+
+vxlan_ping_test()
+{
+       local ping_dev=$1; shift
+       local ping_dip=$1; shift
+       local ping_args=$1; shift
+       local capture_dev=$1; shift
+       local capture_dir=$1; shift
+       local capture_pref=$1; shift
+       local expect=$1; shift
+
+       local t0=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       ping_do $ping_dev $ping_dip "$ping_args"
+       local t1=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "$capture_dev: Expected to capture $expect packets, got $delta."
+}
+
+test_ttl()
+{
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_ttl 99 action pass
+       vxlan_ping_test $h1 192.0.2.3 "" v1 egress 77 10
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: envelope TTL"
+}
+
+test_tos()
+{
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_tos 0x40 action pass
+       vxlan_ping_test $h1 192.0.2.3 "-Q 0x40" v1 egress 77 10
+       vxlan_ping_test $h1 192.0.2.3 "-Q 0x30" v1 egress 77 0
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: envelope TOS inheritance"
+}
+
+__test_ecn_encap()
+{
+       local q=$1; shift
+       local tos=$1; shift
+
+       RET=0
+
+       tc filter add dev v1 egress pref 77 prot ip \
+               flower ip_tos $tos action pass
+       sleep 1
+       vxlan_ping_test $h1 192.0.2.3 "-Q $q" v1 egress 77 10
+       tc filter del dev v1 egress pref 77 prot ip
+
+       log_test "VXLAN: ECN encap: $q->$tos"
+}
+
+test_ecn_encap()
+{
+       # In accordance with INET_ECN_encapsulate()
+       __test_ecn_encap 0x00 0x00
+       __test_ecn_encap 0x01 0x01
+       __test_ecn_encap 0x02 0x02
+       __test_ecn_encap 0x03 0x02
+}
+
+vxlan_encapped_ping_do()
+{
+       local count=$1; shift
+       local dev=$1; shift
+       local next_hop_mac=$1; shift
+       local dest_ip=$1; shift
+       local dest_mac=$1; shift
+       local inner_tos=$1; shift
+       local outer_tos=$1; shift
+
+       $MZ $dev -c $count -d 100msec -q \
+               -b $next_hop_mac -B $dest_ip \
+               -t udp tos=$outer_tos,sp=23456,dp=$VXPORT,p=$(:
+                   )"08:"$(                      : VXLAN flags
+                   )"00:00:00:"$(                : VXLAN reserved
+                   )"00:03:e8:"$(                : VXLAN VNI
+                   )"00:"$(                      : VXLAN reserved
+                   )"$dest_mac:"$(               : ETH daddr
+                   )"$(mac_get w2):"$(           : ETH saddr
+                   )"08:00:"$(                   : ETH type
+                   )"45:"$(                      : IP version + IHL
+                   )"$inner_tos:"$(              : IP TOS
+                   )"00:54:"$(                   : IP total length
+                   )"99:83:"$(                   : IP identification
+                   )"40:00:"$(                   : IP flags + frag off
+                   )"40:"$(                      : IP TTL
+                   )"01:"$(                      : IP proto
+                   )"00:00:"$(                   : IP header csum
+                   )"c0:00:02:03:"$(             : IP saddr: 192.0.2.3
+                   )"c0:00:02:01:"$(             : IP daddr: 192.0.2.1
+                   )"08:"$(                      : ICMP type
+                   )"00:"$(                      : ICMP code
+                   )"8b:f2:"$(                   : ICMP csum
+                   )"1f:6a:"$(                   : ICMP request identifier
+                   )"00:01:"$(                   : ICMP request sequence number
+                   )"4f:ff:c5:5b:00:00:00:00:"$( : ICMP payload
+                   )"6d:74:0b:00:00:00:00:00:"$( :
+                   )"10:11:12:13:14:15:16:17:"$( :
+                   )"18:19:1a:1b:1c:1d:1e:1f:"$( :
+                   )"20:21:22:23:24:25:26:27:"$( :
+                   )"28:29:2a:2b:2c:2d:2e:2f:"$( :
+                   )"30:31:32:33:34:35:36:37"
+}
+export -f vxlan_encapped_ping_do
+
+vxlan_encapped_ping_test()
+{
+       local ping_dev=$1; shift
+       local nh_dev=$1; shift
+       local ping_dip=$1; shift
+       local inner_tos=$1; shift
+       local outer_tos=$1; shift
+       local stat_get=$1; shift
+       local expect=$1; shift
+
+       local t0=$($stat_get)
+
+       in_ns ns1 \
+               vxlan_encapped_ping_do 10 $ping_dev $(mac_get $nh_dev) \
+                       $ping_dip $(mac_get $h1) \
+                       $inner_tos $outer_tos
+
+       local t1=$($stat_get)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "Expected to capture $expect packets, got $delta."
+}
+export -f vxlan_encapped_ping_test
+
+__test_ecn_decap()
+{
+       local orig_inner_tos=$1; shift
+       local orig_outer_tos=$1; shift
+       local decapped_tos=$1; shift
+
+       RET=0
+
+       tc filter add dev $h1 ingress pref 77 prot ip \
+               flower ip_tos $decapped_tos action pass
+       sleep 1
+       vxlan_encapped_ping_test v2 v1 192.0.2.17 \
+                                $orig_inner_tos $orig_outer_tos \
+                                "tc_rule_stats_get $h1 77 ingress" 10
+       tc filter del dev $h1 ingress pref 77
+
+       log_test "VXLAN: ECN decap: $orig_outer_tos/$orig_inner_tos->$decapped_tos"
+}
+
+test_ecn_decap_error()
+{
+       local orig_inner_tos=00
+       local orig_outer_tos=03
+
+       RET=0
+
+       vxlan_encapped_ping_test v2 v1 192.0.2.17 \
+                                $orig_inner_tos $orig_outer_tos \
+                                "link_stats_rx_errors_get vx1" 10
+
+       log_test "VXLAN: ECN decap: $orig_outer_tos/$orig_inner_tos->error"
+}
+
+test_ecn_decap()
+{
+       # In accordance with INET_ECN_decapsulate()
+       __test_ecn_decap 00 00 0x00
+       __test_ecn_decap 01 01 0x01
+       __test_ecn_decap 02 01 0x02
+       __test_ecn_decap 01 03 0x03
+       __test_ecn_decap 02 03 0x03
+       test_ecn_decap_error
+}
+
+test_learning()
+{
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+
+       # Enable learning on the VxLAN device and set ageing time to 10 seconds
+       ip link set dev br1 type bridge ageing_time 1000
+       ip link set dev vx1 type vxlan ageing 10
+       ip link set dev vx1 type vxlan learning
+       reapply_config
+
+       # Check that flooding works
+       RET=0
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: flood before learning"
+
+       # Send a packet with source mac set to $mac from host w2 and check that
+       # a corresponding entry is created in VxLAN device vx1
+       RET=0
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: show learned FDB entry"
+
+       # Repeat first test and check that packets only reach host w2 in ns1
+       RET=0
+
+       vxlan_flood_test $mac $dst 0 10 0
+
+       log_test "VXLAN: learned FDB entry"
+
+       # Delete the learned FDB entry from the VxLAN and bridge devices and
+       # check that packets are flooded
+       RET=0
+
+       bridge fdb del dev vx1 $mac master self
+       sleep 1
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: deletion of learned FDB entry"
+
+       # Re-learn the first FDB entry and check that it is correctly aged-out
+       RET=0
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       vxlan_flood_test $mac $dst 0 10 0
+
+       sleep 20
+
+       bridge fdb show brport vx1 | grep $mac | grep -q self
+       check_fail $?
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_fail $?
+
+       vxlan_flood_test $mac $dst 10 10 10
+
+       log_test "VXLAN: Ageing of learned FDB entry"
+
+       # Toggle learning on the bridge port and check that the bridge's FDB
+       # is populated only when it should
+       RET=0
+
+       ip link set dev vx1 type bridge_slave learning off
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_fail $?
+
+       ip link set dev vx1 type bridge_slave learning on
+
+       in_ns ns1 $MZ w2 -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff -B $dst \
+               -t icmp -q
+       sleep 1
+
+       bridge fdb show brport vx1 | grep $mac | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: learning toggling on bridge port"
+
+       # Restore previous settings
+       ip link set dev vx1 type vxlan nolearning
+       ip link set dev vx1 type vxlan ageing 300
+       ip link set dev br1 type bridge ageing_time 30000
+       reapply_config
+}
+
+test_all()
+{
+       echo "Running tests with UDP port $VXPORT"
+       tests_run
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+test_all
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1d_port_8472.sh
new file mode 100755 (executable)
index 0000000..3bf3da6
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# A wrapper to run VXLAN tests with an unusual port number.
+
+VXPORT=8472
+ALL_TESTS="
+       ping_ipv4
+"
+source vxlan_bridge_1d.sh
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q.sh
new file mode 100755 (executable)
index 0000000..a578972
--- /dev/null
@@ -0,0 +1,860 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# +-----------------------+                          +------------------------+
+# | H1 (vrf)              |                          | H2 (vrf)               |
+# |  + $h1.10             |                          |  + $h2.10              |
+# |  | 192.0.2.1/28       |                          |  | 192.0.2.2/28        |
+# |  |                    |                          |  |                     |
+# |  | + $h1.20           |                          |  | + $h2.20            |
+# |  \ | 198.51.100.1/24  |                          |  \ | 198.51.100.2/24   |
+# |   \|                  |                          |   \|                   |
+# |    + $h1              |                          |    + $h2               |
+# +----|------------------+                          +----|-------------------+
+#      |                                                  |
+# +----|--------------------------------------------------|-------------------+
+# | SW |                                                  |                   |
+# | +--|--------------------------------------------------|-----------------+ |
+# | |  + $swp1                   BR1 (802.1q)             + $swp2           | |
+# | |     vid 10                                             vid 10         | |
+# | |     vid 20                                             vid 20         | |
+# | |                                                                       | |
+# | |  + vx10 (vxlan)                        + vx20 (vxlan)                 | |
+# | |    local 192.0.2.17                      local 192.0.2.17             | |
+# | |    remote 192.0.2.34 192.0.2.50          remote 192.0.2.34 192.0.2.50 | |
+# | |    id 1000 dstport $VXPORT               id 2000 dstport $VXPORT      | |
+# | |    vid 10 pvid untagged                  vid 20 pvid untagged         | |
+# | +-----------------------------------------------------------------------+ |
+# |                                                                           |
+# |  192.0.2.32/28 via 192.0.2.18                                             |
+# |  192.0.2.48/28 via 192.0.2.18                                             |
+# |                                                                           |
+# |    + $rp1                                                                 |
+# |    | 192.0.2.17/28                                                        |
+# +----|----------------------------------------------------------------------+
+#      |
+# +----|--------------------------------------------------------+
+# |    |                                             VRP2 (vrf) |
+# |    + $rp2                                                   |
+# |      192.0.2.18/28                                          |
+# |                                                             |   (maybe) HW
+# =============================================================================
+# |                                                             |  (likely) SW
+# |    + v1 (veth)                             + v3 (veth)      |
+# |    | 192.0.2.33/28                         | 192.0.2.49/28  |
+# +----|---------------------------------------|----------------+
+#      |                                       |
+# +----|------------------------------+   +----|------------------------------+
+# |    + v2 (veth)        NS1 (netns) |   |    + v4 (veth)        NS2 (netns) |
+# |      192.0.2.34/28                |   |      192.0.2.50/28                |
+# |                                   |   |                                   |
+# |   192.0.2.16/28 via 192.0.2.33    |   |   192.0.2.16/28 via 192.0.2.49    |
+# |   192.0.2.50/32 via 192.0.2.33    |   |   192.0.2.34/32 via 192.0.2.49    |
+# |                                   |   |                                   |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# | |                  BR2 (802.1q) | |   | |                  BR2 (802.1q) | |
+# | |  + vx10 (vxlan)               | |   | |  + vx10 (vxlan)               | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 1000 dstport $VXPORT    | |   | |    id 1000 dstport $VXPORT    | |
+# | |    vid 10 pvid untagged       | |   | |    vid 10 pvid untagged       | |
+# | |                               | |   | |                               | |
+# | |  + vx20 (vxlan)               | |   | |  + vx20 (vxlan)               | |
+# | |    local 192.0.2.34           | |   | |    local 192.0.2.50           | |
+# | |    remote 192.0.2.17          | |   | |    remote 192.0.2.17          | |
+# | |    remote 192.0.2.50          | |   | |    remote 192.0.2.34          | |
+# | |    id 2000 dstport $VXPORT    | |   | |    id 2000 dstport $VXPORT    | |
+# | |    vid 20 pvid untagged       | |   | |    vid 20 pvid untagged       | |
+# | |                               | |   | |                               | |
+# | |  + w1 (veth)                  | |   | |  + w1 (veth)                  | |
+# | |  | vid 10                     | |   | |  | vid 10                     | |
+# | |  | vid 20                     | |   | |  | vid 20                     | |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# |    |                              |   |    |                              |
+# | +--|----------------------------+ |   | +--|----------------------------+ |
+# | |  + w2 (veth)        VW2 (vrf) | |   | |  + w2 (veth)        VW2 (vrf) | |
+# | |  |\                           | |   | |  |\                           | |
+# | |  | + w2.10                    | |   | |  | + w2.10                    | |
+# | |  |   192.0.2.3/28             | |   | |  |   192.0.2.4/28             | |
+# | |  |                            | |   | |  |                            | |
+# | |  + w2.20                      | |   | |  + w2.20                      | |
+# | |    198.51.100.3/24            | |   | |    198.51.100.4/24            | |
+# | +-------------------------------+ |   | +-------------------------------+ |
+# +-----------------------------------+   +-----------------------------------+
+
+: ${VXPORT:=4789}
+export VXPORT
+
+: ${ALL_TESTS:="
+       ping_ipv4
+       test_flood
+       test_unicast
+       reapply_config
+       ping_ipv4
+       test_flood
+       test_unicast
+       test_learning
+       test_pvid
+    "}
+
+NUM_NETIFS=6
+source lib.sh
+
+h1_create()
+{
+       simple_if_init $h1
+       tc qdisc add dev $h1 clsact
+       vlan_create $h1 10 v$h1 192.0.2.1/28
+       vlan_create $h1 20 v$h1 198.51.100.1/24
+}
+
+h1_destroy()
+{
+       vlan_destroy $h1 20
+       vlan_destroy $h1 10
+       tc qdisc del dev $h1 clsact
+       simple_if_fini $h1
+}
+
+h2_create()
+{
+       simple_if_init $h2
+       tc qdisc add dev $h2 clsact
+       vlan_create $h2 10 v$h2 192.0.2.2/28
+       vlan_create $h2 20 v$h2 198.51.100.2/24
+}
+
+h2_destroy()
+{
+       vlan_destroy $h2 20
+       vlan_destroy $h2 10
+       tc qdisc del dev $h2 clsact
+       simple_if_fini $h2
+}
+
+rp1_set_addr()
+{
+       ip address add dev $rp1 192.0.2.17/28
+
+       ip route add 192.0.2.32/28 nexthop via 192.0.2.18
+       ip route add 192.0.2.48/28 nexthop via 192.0.2.18
+}
+
+rp1_unset_addr()
+{
+       ip route del 192.0.2.48/28 nexthop via 192.0.2.18
+       ip route del 192.0.2.32/28 nexthop via 192.0.2.18
+
+       ip address del dev $rp1 192.0.2.17/28
+}
+
+switch_create()
+{
+       ip link add name br1 type bridge vlan_filtering 1 vlan_default_pvid 0 \
+               mcast_snooping 0
+       # Make sure the bridge uses the MAC address of the local port and not
+       # that of the VxLAN's device.
+       ip link set dev br1 address $(mac_get $swp1)
+       ip link set dev br1 up
+
+       ip link set dev $rp1 up
+       rp1_set_addr
+
+       ip link add name vx10 type vxlan id 1000        \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx10 up
+
+       ip link set dev vx10 master br1
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link add name vx20 type vxlan id 2000        \
+               local 192.0.2.17 dstport "$VXPORT"      \
+               nolearning noudpcsum tos inherit ttl 100
+       ip link set dev vx20 up
+
+       ip link set dev vx20 master br1
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       ip link set dev $swp1 master br1
+       ip link set dev $swp1 up
+       bridge vlan add vid 10 dev $swp1
+       bridge vlan add vid 20 dev $swp1
+
+       ip link set dev $swp2 master br1
+       ip link set dev $swp2 up
+       bridge vlan add vid 10 dev $swp2
+       bridge vlan add vid 20 dev $swp2
+
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+}
+
+switch_destroy()
+{
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge vlan del vid 20 dev $swp2
+       bridge vlan del vid 10 dev $swp2
+       ip link set dev $swp2 down
+       ip link set dev $swp2 nomaster
+
+       bridge vlan del vid 20 dev $swp1
+       bridge vlan del vid 10 dev $swp1
+       ip link set dev $swp1 down
+       ip link set dev $swp1 nomaster
+
+       bridge vlan del vid 20 dev vx20
+       ip link set dev vx20 nomaster
+
+       ip link set dev vx20 down
+       ip link del dev vx20
+
+       bridge vlan del vid 10 dev vx10
+       ip link set dev vx10 nomaster
+
+       ip link set dev vx10 down
+       ip link del dev vx10
+
+       rp1_unset_addr
+       ip link set dev $rp1 down
+
+       ip link set dev br1 down
+       ip link del dev br1
+}
+
+vrp2_create()
+{
+       simple_if_init $rp2 192.0.2.18/28
+       __simple_if_init v1 v$rp2 192.0.2.33/28
+       __simple_if_init v3 v$rp2 192.0.2.49/28
+       tc qdisc add dev v1 clsact
+}
+
+vrp2_destroy()
+{
+       tc qdisc del dev v1 clsact
+       __simple_if_fini v3 192.0.2.49/28
+       __simple_if_fini v1 192.0.2.33/28
+       simple_if_fini $rp2 192.0.2.18/28
+}
+
+ns_init_common()
+{
+       local in_if=$1; shift
+       local in_addr=$1; shift
+       local other_in_addr=$1; shift
+       local nh_addr=$1; shift
+       local host_addr1=$1; shift
+       local host_addr2=$1; shift
+
+       ip link set dev $in_if up
+       ip address add dev $in_if $in_addr/28
+       tc qdisc add dev $in_if clsact
+
+       ip link add name br2 type bridge vlan_filtering 1 vlan_default_pvid 0
+       ip link set dev br2 up
+
+       ip link add name w1 type veth peer name w2
+
+       ip link set dev w1 master br2
+       ip link set dev w1 up
+
+       bridge vlan add vid 10 dev w1
+       bridge vlan add vid 20 dev w1
+
+       ip link add name vx10 type vxlan id 1000 local $in_addr \
+               dstport "$VXPORT"
+       ip link set dev vx10 up
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx10 master br2
+       tc qdisc add dev vx10 clsact
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link add name vx20 type vxlan id 2000 local $in_addr \
+               dstport "$VXPORT"
+       ip link set dev vx20 up
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.17 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst $other_in_addr self
+
+       ip link set dev vx20 master br2
+       tc qdisc add dev vx20 clsact
+
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       simple_if_init w2
+        vlan_create w2 10 vw2 $host_addr1/28
+        vlan_create w2 20 vw2 $host_addr2/24
+
+       ip route add 192.0.2.16/28 nexthop via $nh_addr
+       ip route add $other_in_addr/32 nexthop via $nh_addr
+}
+export -f ns_init_common
+
+ns1_create()
+{
+       ip netns add ns1
+       ip link set dev v2 netns ns1
+       in_ns ns1 \
+             ns_init_common v2 192.0.2.34 192.0.2.50 192.0.2.33 192.0.2.3 \
+             198.51.100.3
+}
+
+ns1_destroy()
+{
+       ip netns exec ns1 ip link set dev v2 netns 1
+       ip netns del ns1
+}
+
+ns2_create()
+{
+       ip netns add ns2
+       ip link set dev v4 netns ns2
+       in_ns ns2 \
+             ns_init_common v4 192.0.2.50 192.0.2.34 192.0.2.49 192.0.2.4 \
+             198.51.100.4
+}
+
+ns2_destroy()
+{
+       ip netns exec ns2 ip link set dev v4 netns 1
+       ip netns del ns2
+}
+
+setup_prepare()
+{
+       h1=${NETIFS[p1]}
+       swp1=${NETIFS[p2]}
+
+       swp2=${NETIFS[p3]}
+       h2=${NETIFS[p4]}
+
+       rp1=${NETIFS[p5]}
+       rp2=${NETIFS[p6]}
+
+       vrf_prepare
+       forwarding_enable
+
+       h1_create
+       h2_create
+       switch_create
+
+       ip link add name v1 type veth peer name v2
+       ip link add name v3 type veth peer name v4
+       vrp2_create
+       ns1_create
+       ns2_create
+
+       r1_mac=$(in_ns ns1 mac_get w2)
+       r2_mac=$(in_ns ns2 mac_get w2)
+       h2_mac=$(mac_get $h2)
+}
+
+cleanup()
+{
+       pre_cleanup
+
+       ns2_destroy
+       ns1_destroy
+       vrp2_destroy
+       ip link del dev v3
+       ip link del dev v1
+
+       switch_destroy
+       h2_destroy
+       h1_destroy
+
+       forwarding_restore
+       vrf_cleanup
+}
+
+# For the first round of tests, vx10 and vx20 were the first devices to get
+# attached to the bridge, and that at the point that the local IP is already
+# configured. Try the other scenario of attaching these devices to a bridge
+# that already has local ports members, and only then assign the local IP.
+reapply_config()
+{
+       log_info "Reapplying configuration"
+
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+       bridge fdb del dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+
+       ip link set dev vx20 nomaster
+       ip link set dev vx10 nomaster
+
+       rp1_unset_addr
+       sleep 5
+
+       ip link set dev vx10 master br1
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       ip link set dev vx20 master br1
+       bridge vlan add vid 20 dev vx20 pvid untagged
+
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx10 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.34 self
+       bridge fdb append dev vx20 00:00:00:00:00:00 dst 192.0.2.50 self
+
+       rp1_set_addr
+       sleep 5
+}
+
+ping_ipv4()
+{
+       ping_test $h1.10 192.0.2.2 ": local->local vid 10"
+       ping_test $h1.20 198.51.100.2 ": local->local vid 20"
+       ping_test $h1.10 192.0.2.3 ": local->remote 1 vid 10"
+       ping_test $h1.10 192.0.2.4 ": local->remote 2 vid 10"
+       ping_test $h1.20 198.51.100.3 ": local->remote 1 vid 20"
+       ping_test $h1.20 198.51.100.4 ": local->remote 2 vid 20"
+}
+
+maybe_in_ns()
+{
+       echo ${1:+in_ns} $1
+}
+
+__flood_counter_add_del()
+{
+       local add_del=$1; shift
+       local dev=$1; shift
+       local ns=$1; shift
+
+       # Putting the ICMP capture both to HW and to SW will end up
+       # double-counting the packets that are trapped to slow path, such as for
+       # the unicast test. Adding either skip_hw or skip_sw fixes this problem,
+       # but with skip_hw, the flooded packets are not counted at all, because
+       # those are dropped due to MAC address mismatch; and skip_sw is a no-go
+       # for veth-based topologies.
+       #
+       # So try to install with skip_sw and fall back to skip_sw if that fails.
+
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_sw 2>/dev/null || \
+       $(maybe_in_ns $ns) __icmp_capture_add_del          \
+                          $add_del 100 "" $dev skip_hw
+}
+
+flood_counter_install()
+{
+       __flood_counter_add_del add "$@"
+}
+
+flood_counter_uninstall()
+{
+       __flood_counter_add_del del "$@"
+}
+
+flood_fetch_stat()
+{
+       local dev=$1; shift
+       local ns=$1; shift
+
+       $(maybe_in_ns $ns) tc_rule_stats_get $dev 100 ingress
+}
+
+flood_fetch_stats()
+{
+       local counters=("${@}")
+       local counter
+
+       for counter in "${counters[@]}"; do
+               flood_fetch_stat $counter
+       done
+}
+
+vxlan_flood_test()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local -a expects=("${@}")
+
+       local -a counters=($h2 "vx10 ns1" "vx20 ns1" "vx10 ns2" "vx20 ns2")
+       local counter
+       local key
+
+       # Packets reach the local host tagged whereas they reach the VxLAN
+       # devices untagged. In order to be able to use the same filter for
+       # all counters, make sure the packets also reach the local host
+       # untagged
+       bridge vlan add vid $vid dev $swp2 untagged
+       for counter in "${counters[@]}"; do
+               flood_counter_install $counter
+       done
+
+       local -a t0s=($(flood_fetch_stats "${counters[@]}"))
+       $MZ $h1 -Q $vid -c 10 -d 100msec -p 64 -b $mac -B $dst -t icmp -q
+       sleep 1
+       local -a t1s=($(flood_fetch_stats "${counters[@]}"))
+
+       for key in ${!t0s[@]}; do
+               local delta=$((t1s[$key] - t0s[$key]))
+               local expect=${expects[$key]}
+
+               ((expect == delta))
+               check_err $? "${counters[$key]}: Expected to capture $expect packets, got $delta."
+       done
+
+       for counter in "${counters[@]}"; do
+               flood_counter_uninstall $counter
+       done
+       bridge vlan add vid $vid dev $swp2
+}
+
+__test_flood()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local what=$1; shift
+       local -a expects=("${@}")
+
+       RET=0
+
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_flood()
+{
+       __test_flood de:ad:be:ef:13:37 192.0.2.100 10 "flood vlan 10" \
+               10 10 0 10 0
+       __test_flood ca:fe:be:ef:13:37 198.51.100.100 20 "flood vlan 20" \
+               10 0 10 0 10
+}
+
+vxlan_fdb_add_del()
+{
+       local add_del=$1; shift
+       local vid=$1; shift
+       local mac=$1; shift
+       local dev=$1; shift
+       local dst=$1; shift
+
+       bridge fdb $add_del dev $dev $mac self static permanent \
+               ${dst:+dst} $dst 2>/dev/null
+       bridge fdb $add_del dev $dev $mac master static vlan $vid 2>/dev/null
+}
+
+__test_unicast()
+{
+       local mac=$1; shift
+       local dst=$1; shift
+       local hit_idx=$1; shift
+       local vid=$1; shift
+       local what=$1; shift
+
+       RET=0
+
+       local -a expects=(0 0 0 0 0)
+       expects[$hit_idx]=10
+
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: $what"
+}
+
+test_unicast()
+{
+       local -a targets=("$h2_mac $h2"
+                         "$r1_mac vx10 192.0.2.34"
+                         "$r2_mac vx10 192.0.2.50")
+       local target
+
+       log_info "unicast vlan 10"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add 10 $target
+       done
+
+       __test_unicast $h2_mac 192.0.2.2 0 10 "local MAC unicast"
+       __test_unicast $r1_mac 192.0.2.3 1 10 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 192.0.2.4 3 10 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del 10 $target
+       done
+
+       log_info "unicast vlan 20"
+
+       targets=("$h2_mac $h2" "$r1_mac vx20 192.0.2.34" \
+                "$r2_mac vx20 192.0.2.50")
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del add 20 $target
+       done
+
+       __test_unicast $h2_mac 198.51.100.2 0 20 "local MAC unicast"
+       __test_unicast $r1_mac 198.51.100.3 2 20 "remote MAC 1 unicast"
+       __test_unicast $r2_mac 198.51.100.4 4 20 "remote MAC 2 unicast"
+
+       for target in "${targets[@]}"; do
+               vxlan_fdb_add_del del 20 $target
+       done
+}
+
+test_pvid()
+{
+       local -a expects=(0 0 0 0 0)
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+       local vid=10
+
+       # Check that flooding works
+       RET=0
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood before pvid off"
+
+       # Toggle PVID off and test that flood to remote hosts does not work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10
+
+       expects[0]=10; expects[1]=0; expects[3]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after pvid off"
+
+       # Toggle PVID on and test that flood to remote hosts does work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after pvid on"
+
+       # Add a new VLAN and test that it does not affect flooding
+       RET=0
+
+       bridge vlan add vid 30 dev vx10
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       bridge vlan del vid 30 dev vx10
+
+       log_test "VXLAN: flood after vlan add"
+
+       # Remove currently mapped VLAN and test that flood to remote hosts does
+       # not work
+       RET=0
+
+       bridge vlan del vid 10 dev vx10
+
+       expects[0]=10; expects[1]=0; expects[3]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after vlan delete"
+
+       # Re-add the VLAN and test that flood to remote hosts does work
+       RET=0
+
+       bridge vlan add vid 10 dev vx10 pvid untagged
+
+       expects[0]=10; expects[1]=10; expects[3]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood after vlan re-add"
+}
+
+vxlan_ping_test()
+{
+       local ping_dev=$1; shift
+       local ping_dip=$1; shift
+       local ping_args=$1; shift
+       local capture_dev=$1; shift
+       local capture_dir=$1; shift
+       local capture_pref=$1; shift
+       local expect=$1; shift
+
+       local t0=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       ping_do $ping_dev $ping_dip "$ping_args"
+       local t1=$(tc_rule_stats_get $capture_dev $capture_pref $capture_dir)
+       local delta=$((t1 - t0))
+
+       # Tolerate a couple stray extra packets.
+       ((expect <= delta && delta <= expect + 2))
+       check_err $? "$capture_dev: Expected to capture $expect packets, got $delta."
+}
+
+__test_learning()
+{
+       local -a expects=(0 0 0 0 0)
+       local mac=$1; shift
+       local dst=$1; shift
+       local vid=$1; shift
+       local idx1=$1; shift
+       local idx2=$1; shift
+       local vx=vx$vid
+
+       # Check that flooding works
+       RET=0
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: flood before learning"
+
+       # Send a packet with source mac set to $mac from host w2 and check that
+       # a corresponding entry is created in the VxLAN device
+       RET=0
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: show learned FDB entry"
+
+       # Repeat first test and check that packets only reach host w2 in ns1
+       RET=0
+
+       expects[0]=0; expects[$idx1]=10; expects[$idx2]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: learned FDB entry"
+
+       # Delete the learned FDB entry from the VxLAN and bridge devices and
+       # check that packets are flooded
+       RET=0
+
+       bridge fdb del dev $vx $mac master self vlan $vid
+       sleep 1
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: deletion of learned FDB entry"
+
+       # Re-learn the first FDB entry and check that it is correctly aged-out
+       RET=0
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_err $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       expects[0]=0; expects[$idx1]=10; expects[$idx2]=0
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       sleep 20
+
+       bridge fdb show brport $vx | grep $mac | grep -q self
+       check_fail $?
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_fail $?
+
+       expects[0]=10; expects[$idx1]=10; expects[$idx2]=10
+       vxlan_flood_test $mac $dst $vid "${expects[@]}"
+
+       log_test "VXLAN: Ageing of learned FDB entry"
+
+       # Toggle learning on the bridge port and check that the bridge's FDB
+       # is populated only when it should
+       RET=0
+
+       ip link set dev $vx type bridge_slave learning off
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_fail $?
+
+       ip link set dev $vx type bridge_slave learning on
+
+       in_ns ns1 $MZ w2 -Q $vid -c 1 -p 64 -a $mac -b ff:ff:ff:ff:ff:ff \
+               -B $dst -t icmp -q
+       sleep 1
+
+       bridge fdb show brport $vx | grep $mac | grep "vlan $vid" \
+               | grep -q -v self
+       check_err $?
+
+       log_test "VXLAN: learning toggling on bridge port"
+}
+
+test_learning()
+{
+       local mac=de:ad:be:ef:13:37
+       local dst=192.0.2.100
+       local vid=10
+
+       # Enable learning on the VxLAN devices and set ageing time to 10 seconds
+       ip link set dev br1 type bridge ageing_time 1000
+       ip link set dev vx10 type vxlan ageing 10
+       ip link set dev vx10 type vxlan learning
+       ip link set dev vx20 type vxlan ageing 10
+       ip link set dev vx20 type vxlan learning
+       reapply_config
+
+       log_info "learning vlan 10"
+
+       __test_learning $mac $dst $vid 1 3
+
+       log_info "learning vlan 20"
+
+       mac=ca:fe:be:ef:13:37
+       dst=198.51.100.100
+       vid=20
+
+       __test_learning $mac $dst $vid 2 4
+
+       # Restore previous settings
+       ip link set dev vx20 type vxlan nolearning
+       ip link set dev vx20 type vxlan ageing 300
+       ip link set dev vx10 type vxlan nolearning
+       ip link set dev vx10 type vxlan ageing 300
+       ip link set dev br1 type bridge ageing_time 30000
+       reapply_config
+}
+
+test_all()
+{
+       log_info "Running tests with UDP port $VXPORT"
+       tests_run
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+test_all
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh b/tools/testing/selftests/net/forwarding/vxlan_bridge_1q_port_8472.sh
new file mode 100755 (executable)
index 0000000..b1b2d1a
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# A wrapper to run VXLAN tests with an unusual port number.
+
+VXPORT=8472
+ALL_TESTS="
+       ping_ipv4
+"
+source vxlan_bridge_1q.sh
index 406cc70..4b02933 100644 (file)
@@ -651,12 +651,13 @@ static void do_flush_datagram(int fd, int type)
 
 static void do_rx(int domain, int type, int protocol)
 {
+       const int cfg_receiver_wait_ms = 400;
        uint64_t tstop;
        int fd;
 
        fd = do_setup_rx(domain, type, protocol);
 
-       tstop = gettimeofday_ms() + cfg_runtime_ms;
+       tstop = gettimeofday_ms() + cfg_runtime_ms + cfg_receiver_wait_ms;
        do {
                if (type == SOCK_STREAM)
                        do_flush_tcp(fd);
index c43c6de..825ffec 100755 (executable)
@@ -25,6 +25,8 @@ readonly path_sysctl_mem="net.core.optmem_max"
 if [[ "$#" -eq "0" ]]; then
        $0 4 tcp -t 1
        $0 6 tcp -t 1
+       $0 4 udp -t 1
+       $0 6 udp -t 1
        echo "OK. All tests passed"
        exit 0
 fi
index a369d61..e2c94e4 100755 (executable)
 # - pmtu_ipv6
 #      Same as pmtu_ipv4, except for locked PMTU tests, using IPv6
 #
+# - pmtu_ipv4_vxlan4_exception
+#      Set up the same network topology as pmtu_ipv4, create a VXLAN tunnel
+#      over IPv4 between A and B, routed via R1. On the link between R1 and B,
+#      set a MTU lower than the VXLAN MTU and the MTU on the link between A and
+#      R1. Send IPv4 packets, exceeding the MTU between R1 and B, over VXLAN
+#      from A to B and check that the PMTU exception is created with the right
+#      value on A
+#
+# - pmtu_ipv6_vxlan4_exception
+#      Same as pmtu_ipv4_vxlan4_exception, but send IPv6 packets from A to B
+#
+# - pmtu_ipv4_vxlan6_exception
+#      Same as pmtu_ipv4_vxlan4_exception, but use IPv6 transport from A to B
+#
+# - pmtu_ipv6_vxlan6_exception
+#      Same as pmtu_ipv4_vxlan6_exception, but send IPv6 packets from A to B
+#
+# - pmtu_ipv4_geneve4_exception
+#      Same as pmtu_ipv4_vxlan4_exception, but using a GENEVE tunnel instead of
+#      VXLAN
+#
+# - pmtu_ipv6_geneve4_exception
+#      Same as pmtu_ipv6_vxlan4_exception, but using a GENEVE tunnel instead of
+#      VXLAN
+#
+# - pmtu_ipv4_geneve6_exception
+#      Same as pmtu_ipv4_vxlan6_exception, but using a GENEVE tunnel instead of
+#      VXLAN
+#
+# - pmtu_ipv6_geneve6_exception
+#      Same as pmtu_ipv6_vxlan6_exception, but using a GENEVE tunnel instead of
+#      VXLAN
+#
+# - pmtu_ipv{4,6}_fou{4,6}_exception
+#      Same as pmtu_ipv4_vxlan4, but using a direct IPv4/IPv6 encapsulation
+#      (FoU) over IPv4/IPv6, instead of VXLAN
+#
+# - pmtu_ipv{4,6}_fou{4,6}_exception
+#      Same as pmtu_ipv4_vxlan4, but using a generic UDP IPv4/IPv6
+#      encapsulation (GUE) over IPv4/IPv6, instead of VXLAN
+#
 # - pmtu_vti4_exception
 #      Set up vti tunnel on top of veth, with xfrm states and policies, in two
 #      namespaces with matching endpoints. Check that route exception is not
@@ -72,6 +113,22 @@ which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
 tests="
        pmtu_ipv4_exception             ipv4: PMTU exceptions
        pmtu_ipv6_exception             ipv6: PMTU exceptions
+       pmtu_ipv4_vxlan4_exception      IPv4 over vxlan4: PMTU exceptions
+       pmtu_ipv6_vxlan4_exception      IPv6 over vxlan4: PMTU exceptions
+       pmtu_ipv4_vxlan6_exception      IPv4 over vxlan6: PMTU exceptions
+       pmtu_ipv6_vxlan6_exception      IPv6 over vxlan6: PMTU exceptions
+       pmtu_ipv4_geneve4_exception     IPv4 over geneve4: PMTU exceptions
+       pmtu_ipv6_geneve4_exception     IPv6 over geneve4: PMTU exceptions
+       pmtu_ipv4_geneve6_exception     IPv4 over geneve6: PMTU exceptions
+       pmtu_ipv6_geneve6_exception     IPv6 over geneve6: PMTU exceptions
+       pmtu_ipv4_fou4_exception        IPv4 over fou4: PMTU exceptions
+       pmtu_ipv6_fou4_exception        IPv6 over fou4: PMTU exceptions
+       pmtu_ipv4_fou6_exception        IPv4 over fou6: PMTU exceptions
+       pmtu_ipv6_fou6_exception        IPv6 over fou6: PMTU exceptions
+       pmtu_ipv4_gue4_exception        IPv4 over gue4: PMTU exceptions
+       pmtu_ipv6_gue4_exception        IPv6 over gue4: PMTU exceptions
+       pmtu_ipv4_gue6_exception        IPv4 over gue6: PMTU exceptions
+       pmtu_ipv6_gue6_exception        IPv6 over gue6: PMTU exceptions
        pmtu_vti6_exception             vti6: PMTU exceptions
        pmtu_vti4_exception             vti4: PMTU exceptions
        pmtu_vti4_default_mtu           vti4: default MTU assignment
@@ -95,8 +152,8 @@ ns_r2="ip netns exec ${NS_R2}"
 # Addresses are:
 # - IPv4: PREFIX4.SEGMENT.ID (/24)
 # - IPv6: PREFIX6:SEGMENT::ID (/64)
-prefix4="192.168"
-prefix6="fd00"
+prefix4="10.0"
+prefix6="fc00"
 a_r1=1
 a_r2=2
 b_r1=3
@@ -129,12 +186,12 @@ veth6_a_addr="fd00:1::a"
 veth6_b_addr="fd00:1::b"
 veth6_mask="64"
 
-vti4_a_addr="192.168.2.1"
-vti4_b_addr="192.168.2.2"
-vti4_mask="24"
-vti6_a_addr="fd00:2::a"
-vti6_b_addr="fd00:2::b"
-vti6_mask="64"
+tunnel4_a_addr="192.168.2.1"
+tunnel4_b_addr="192.168.2.2"
+tunnel4_mask="24"
+tunnel6_a_addr="fd00:2::a"
+tunnel6_b_addr="fd00:2::b"
+tunnel6_mask="64"
 
 dummy6_0_addr="fc00:1000::0"
 dummy6_1_addr="fc00:1001::0"
@@ -159,6 +216,89 @@ nsname() {
        eval echo \$NS_$1
 }
 
+setup_fou_or_gue() {
+       outer="${1}"
+       inner="${2}"
+       encap="${3}"
+
+       if [ "${outer}" = "4" ]; then
+               modprobe fou || return 2
+               a_addr="${prefix4}.${a_r1}.1"
+               b_addr="${prefix4}.${b_r1}.1"
+               if [ "${inner}" = "4" ]; then
+                       type="ipip"
+                       ipproto="4"
+               else
+                       type="sit"
+                       ipproto="41"
+               fi
+       else
+               modprobe fou6 || return 2
+               a_addr="${prefix6}:${a_r1}::1"
+               b_addr="${prefix6}:${b_r1}::1"
+               if [ "${inner}" = "4" ]; then
+                       type="ip6tnl"
+                       mode="mode ipip6"
+                       ipproto="4 -6"
+               else
+                       type="ip6tnl"
+                       mode="mode ip6ip6"
+                       ipproto="41 -6"
+               fi
+       fi
+
+       ${ns_a} ip fou add port 5555 ipproto ${ipproto} || return 2
+       ${ns_a} ip link add ${encap}_a type ${type} ${mode} local ${a_addr} remote ${b_addr} encap ${encap} encap-sport auto encap-dport 5556 || return 2
+
+       ${ns_b} ip fou add port 5556 ipproto ${ipproto}
+       ${ns_b} ip link add ${encap}_b type ${type} ${mode} local ${b_addr} remote ${a_addr} encap ${encap} encap-sport auto encap-dport 5555
+
+       if [ "${inner}" = "4" ]; then
+               ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ${encap}_a
+               ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${encap}_b
+       else
+               ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ${encap}_a
+               ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${encap}_b
+       fi
+
+       ${ns_a} ip link set ${encap}_a up
+       ${ns_b} ip link set ${encap}_b up
+
+       sleep 1
+}
+
+setup_fou44() {
+       setup_fou_or_gue 4 4 fou
+}
+
+setup_fou46() {
+       setup_fou_or_gue 4 6 fou
+}
+
+setup_fou64() {
+       setup_fou_or_gue 6 4 fou
+}
+
+setup_fou66() {
+       setup_fou_or_gue 6 6 fou
+}
+
+setup_gue44() {
+       setup_fou_or_gue 4 4 gue
+}
+
+setup_gue46() {
+       setup_fou_or_gue 4 6 gue
+}
+
+setup_gue64() {
+       setup_fou_or_gue 6 4 gue
+}
+
+setup_gue66() {
+       setup_fou_or_gue 6 6 gue
+}
+
 setup_namespaces() {
        for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
                ip netns add ${n} || return 1
@@ -202,11 +342,57 @@ setup_vti() {
 }
 
 setup_vti4() {
-       setup_vti 4 ${veth4_a_addr} ${veth4_b_addr} ${vti4_a_addr} ${vti4_b_addr} ${vti4_mask}
+       setup_vti 4 ${veth4_a_addr} ${veth4_b_addr} ${tunnel4_a_addr} ${tunnel4_b_addr} ${tunnel4_mask}
 }
 
 setup_vti6() {
-       setup_vti 6 ${veth6_a_addr} ${veth6_b_addr} ${vti6_a_addr} ${vti6_b_addr} ${vti6_mask}
+       setup_vti 6 ${veth6_a_addr} ${veth6_b_addr} ${tunnel6_a_addr} ${tunnel6_b_addr} ${tunnel6_mask}
+}
+
+setup_vxlan_or_geneve() {
+       type="${1}"
+       a_addr="${2}"
+       b_addr="${3}"
+       opts="${4}"
+
+       if [ "${type}" = "vxlan" ]; then
+               opts="${opts} ttl 64 dstport 4789"
+               opts_a="local ${a_addr}"
+               opts_b="local ${b_addr}"
+       else
+               opts_a=""
+               opts_b=""
+       fi
+
+       ${ns_a} ip link add ${type}_a type ${type} id 1 ${opts_a} remote ${b_addr} ${opts} || return 1
+       ${ns_b} ip link add ${type}_b type ${type} id 1 ${opts_b} remote ${a_addr} ${opts}
+
+       ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ${type}_a
+       ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${type}_b
+
+       ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ${type}_a
+       ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${type}_b
+
+       ${ns_a} ip link set ${type}_a up
+       ${ns_b} ip link set ${type}_b up
+
+       sleep 1
+}
+
+setup_geneve4() {
+       setup_vxlan_or_geneve geneve ${prefix4}.${a_r1}.1  ${prefix4}.${b_r1}.1  "df set"
+}
+
+setup_vxlan4() {
+       setup_vxlan_or_geneve vxlan  ${prefix4}.${a_r1}.1  ${prefix4}.${b_r1}.1  "df set"
+}
+
+setup_geneve6() {
+       setup_vxlan_or_geneve geneve ${prefix6}:${a_r1}::1 ${prefix6}:${b_r1}::1
+}
+
+setup_vxlan6() {
+       setup_vxlan_or_geneve vxlan  ${prefix6}:${a_r1}::1 ${prefix6}:${b_r1}::1
 }
 
 setup_xfrm() {
@@ -465,6 +651,161 @@ test_pmtu_ipv6_exception() {
        test_pmtu_ipvX 6
 }
 
+test_pmtu_ipvX_over_vxlanY_or_geneveY_exception() {
+       type=${1}
+       family=${2}
+       outer_family=${3}
+       ll_mtu=4000
+
+       if [ ${outer_family} -eq 4 ]; then
+               setup namespaces routing ${type}4 || return 2
+               #                      IPv4 header   UDP header   VXLAN/GENEVE header   Ethernet header
+               exp_mtu=$((${ll_mtu} - 20          - 8          - 8                   - 14))
+       else
+               setup namespaces routing ${type}6 || return 2
+               #                      IPv6 header   UDP header   VXLAN/GENEVE header   Ethernet header
+               exp_mtu=$((${ll_mtu} - 40          - 8          - 8                   - 14))
+       fi
+
+       trace "${ns_a}" ${type}_a    "${ns_b}"  ${type}_b \
+             "${ns_a}" veth_A-R1    "${ns_r1}" veth_R1-A \
+             "${ns_b}" veth_B-R1    "${ns_r1}" veth_R1-B
+
+       if [ ${family} -eq 4 ]; then
+               ping=ping
+               dst=${tunnel4_b_addr}
+       else
+               ping=${ping6}
+               dst=${tunnel6_b_addr}
+       fi
+
+       # Create route exception by exceeding link layer MTU
+       mtu "${ns_a}"  veth_A-R1 $((${ll_mtu} + 1000))
+       mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
+       mtu "${ns_b}"  veth_B-R1 ${ll_mtu}
+       mtu "${ns_r1}" veth_R1-B ${ll_mtu}
+
+       mtu "${ns_a}" ${type}_a $((${ll_mtu} + 1000))
+       mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s $((${ll_mtu} + 500)) ${dst} > /dev/null
+
+       # Check that exception was created
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
+       check_pmtu_value ${exp_mtu} "${pmtu}" "exceeding link layer MTU on ${type} interface"
+}
+
+test_pmtu_ipv4_vxlan4_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception vxlan  4 4
+}
+
+test_pmtu_ipv6_vxlan4_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception vxlan  6 4
+}
+
+test_pmtu_ipv4_geneve4_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception geneve 4 4
+}
+
+test_pmtu_ipv6_geneve4_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception geneve 6 4
+}
+
+test_pmtu_ipv4_vxlan6_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception vxlan  4 6
+}
+
+test_pmtu_ipv6_vxlan6_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception vxlan  6 6
+}
+
+test_pmtu_ipv4_geneve6_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception geneve 4 6
+}
+
+test_pmtu_ipv6_geneve6_exception() {
+       test_pmtu_ipvX_over_vxlanY_or_geneveY_exception geneve 6 6
+}
+
+test_pmtu_ipvX_over_fouY_or_gueY() {
+       inner_family=${1}
+       outer_family=${2}
+       encap=${3}
+       ll_mtu=4000
+
+       setup namespaces routing ${encap}${outer_family}${inner_family} || return 2
+       trace "${ns_a}" ${encap}_a   "${ns_b}"  ${encap}_b \
+             "${ns_a}" veth_A-R1    "${ns_r1}" veth_R1-A \
+             "${ns_b}" veth_B-R1    "${ns_r1}" veth_R1-B
+
+       if [ ${inner_family} -eq 4 ]; then
+               ping=ping
+               dst=${tunnel4_b_addr}
+       else
+               ping=${ping6}
+               dst=${tunnel6_b_addr}
+       fi
+
+       if [ "${encap}" = "gue" ]; then
+               encap_overhead=4
+       else
+               encap_overhead=0
+       fi
+
+       if [ ${outer_family} -eq 4 ]; then
+               #                      IPv4 header   UDP header
+               exp_mtu=$((${ll_mtu} - 20          - 8         - ${encap_overhead}))
+       else
+               #                      IPv6 header   Option 4   UDP header
+               exp_mtu=$((${ll_mtu} - 40          - 8        - 8       - ${encap_overhead}))
+       fi
+
+       # Create route exception by exceeding link layer MTU
+       mtu "${ns_a}"  veth_A-R1 $((${ll_mtu} + 1000))
+       mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
+       mtu "${ns_b}"  veth_B-R1 ${ll_mtu}
+       mtu "${ns_r1}" veth_R1-B ${ll_mtu}
+
+       mtu "${ns_a}" ${encap}_a $((${ll_mtu} + 1000))
+       mtu "${ns_b}" ${encap}_b $((${ll_mtu} + 1000))
+       ${ns_a} ${ping} -q -M want -i 0.1 -w 2 -s $((${ll_mtu} + 500)) ${dst} > /dev/null
+
+       # Check that exception was created
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
+       check_pmtu_value ${exp_mtu} "${pmtu}" "exceeding link layer MTU on ${encap} interface"
+}
+
+test_pmtu_ipv4_fou4_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 4 4 fou
+}
+
+test_pmtu_ipv6_fou4_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 6 4 fou
+}
+
+test_pmtu_ipv4_fou6_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 4 6 fou
+}
+
+test_pmtu_ipv6_fou6_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 6 6 fou
+}
+
+test_pmtu_ipv4_gue4_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 4 4 gue
+}
+
+test_pmtu_ipv6_gue4_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 6 4 gue
+}
+
+test_pmtu_ipv4_gue6_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 4 6 gue
+}
+
+test_pmtu_ipv6_gue6_exception() {
+       test_pmtu_ipvX_over_fouY_or_gueY 6 6 gue
+}
+
 test_pmtu_vti4_exception() {
        setup namespaces veth vti4 xfrm4 || return 2
        trace "${ns_a}" veth_a    "${ns_b}" veth_b \
@@ -484,14 +825,14 @@ test_pmtu_vti4_exception() {
 
        # Send DF packet without exceeding link layer MTU, check that no
        # exception is created
-       ${ns_a} ping -q -M want -i 0.1 -w 2 -s ${ping_payload} ${vti4_b_addr} > /dev/null
-       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})"
+       ${ns_a} ping -q -M want -i 0.1 -w 2 -s ${ping_payload} ${tunnel4_b_addr} > /dev/null
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
        check_pmtu_value "" "${pmtu}" "sending packet smaller than PMTU (IP payload length ${esp_payload_rfc4106})" || return 1
 
        # Now exceed link layer MTU by one byte, check that exception is created
        # with the right PMTU value
-       ${ns_a} ping -q -M want -i 0.1 -w 2 -s $((ping_payload + 1)) ${vti4_b_addr} > /dev/null
-       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})"
+       ${ns_a} ping -q -M want -i 0.1 -w 2 -s $((ping_payload + 1)) ${tunnel4_b_addr} > /dev/null
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel4_b_addr})"
        check_pmtu_value "${esp_payload_rfc4106}" "${pmtu}" "exceeding PMTU (IP payload length $((esp_payload_rfc4106 + 1)))"
 }
 
@@ -506,20 +847,20 @@ test_pmtu_vti6_exception() {
        mtu "${ns_b}" veth_b 4000
        mtu "${ns_a}" vti6_a 5000
        mtu "${ns_b}" vti6_b 5000
-       ${ns_a} ${ping6} -q -i 0.1 -w 2 -s 60000 ${vti6_b_addr} > /dev/null
+       ${ns_a} ${ping6} -q -i 0.1 -w 2 -s 60000 ${tunnel6_b_addr} > /dev/null
 
        # Check that exception was created
-       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})"
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
        check_pmtu_value any "${pmtu}" "creating tunnel exceeding link layer MTU" || return 1
 
        # Decrease tunnel MTU, check for PMTU decrease in route exception
        mtu "${ns_a}" vti6_a 3000
-       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})"
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
        check_pmtu_value "3000" "${pmtu}" "decreasing tunnel MTU" || fail=1
 
        # Increase tunnel MTU, check for PMTU increase in route exception
        mtu "${ns_a}" vti6_a 9000
-       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})"
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${tunnel6_b_addr})"
        check_pmtu_value "9000" "${pmtu}" "increasing tunnel MTU" || fail=1
 
        return ${fail}
index bea079e..2dc95fd 100755 (executable)
@@ -25,3 +25,13 @@ if [ $? -ne 0 ]; then
 else
        echo "[PASS]"
 fi
+
+echo "--------------------"
+echo "running txring_overwrite test"
+echo "--------------------"
+./in_netns.sh ./txring_overwrite
+if [ $? -ne 0 ]; then
+       echo "[FAIL]"
+else
+       echo "[PASS]"
+fi
diff --git a/tools/testing/selftests/net/test_vxlan_under_vrf.sh b/tools/testing/selftests/net/test_vxlan_under_vrf.sh
new file mode 100755 (executable)
index 0000000..09f9ed9
--- /dev/null
@@ -0,0 +1,129 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# This test is for checking VXLAN underlay in a non-default VRF.
+#
+# It simulates two hypervisors running a VM each using four network namespaces:
+# two for the HVs, two for the VMs.
+# A small VXLAN tunnel is made between the two hypervisors to have the two vms
+# in the same virtual L2:
+#
+# +-------------------+                                    +-------------------+
+# |                   |                                    |                   |
+# |    vm-1 netns     |                                    |    vm-2 netns     |
+# |                   |                                    |                   |
+# |  +-------------+  |                                    |  +-------------+  |
+# |  |   veth-hv   |  |                                    |  |   veth-hv   |  |
+# |  | 10.0.0.1/24 |  |                                    |  | 10.0.0.2/24 |  |
+# |  +-------------+  |                                    |  +-------------+  |
+# |        .          |                                    |         .         |
+# +-------------------+                                    +-------------------+
+#          .                                                         .
+#          .                                                         .
+#          .                                                         .
+# +-----------------------------------+   +------------------------------------+
+# |        .                          |   |                          .         |
+# |  +----------+                     |   |                     +----------+   |
+# |  | veth-tap |                     |   |                     | veth-tap |   |
+# |  +----+-----+                     |   |                     +----+-----+   |
+# |       |                           |   |                          |         |
+# |    +--+--+      +--------------+  |   |  +--------------+     +--+--+      |
+# |    | br0 |      | vrf-underlay |  |   |  | vrf-underlay |     | br0 |      |
+# |    +--+--+      +-------+------+  |   |  +------+-------+     +--+--+      |
+# |       |                 |         |   |         |                |         |
+# |   +---+----+    +-------+-------+ |   | +-------+-------+    +---+----+    |
+# |   | vxlan0 |....|     veth0     |.|...|.|     veth0     |....| vxlan0 |    |
+# |   +--------+    | 172.16.0.1/24 | |   | | 172.16.0.2/24 |    +--------+    |
+# |                 +---------------+ |   | +---------------+                  |
+# |                                   |   |                                    |
+# |             hv-1 netns            |   |           hv-2 netns               |
+# |                                   |   |                                    |
+# +-----------------------------------+   +------------------------------------+
+#
+# This tests both the connectivity between vm-1 and vm-2, and that the underlay
+# can be moved in and out of the vrf by unsetting and setting veth0's master.
+
+set -e
+
+cleanup() {
+    ip link del veth-hv-1 2>/dev/null || true
+    ip link del veth-tap 2>/dev/null || true
+
+    for ns in hv-1 hv-2 vm-1 vm-2; do
+        ip netns del $ns || true
+    done
+}
+
+# Clean start
+cleanup &> /dev/null
+
+[[ $1 == "clean" ]] && exit 0
+
+trap cleanup EXIT
+
+# Setup "Hypervisors" simulated with netns
+ip link add veth-hv-1 type veth peer name veth-hv-2
+setup-hv-networking() {
+    hv=$1
+
+    ip netns add hv-$hv
+    ip link set veth-hv-$hv netns hv-$hv
+    ip -netns hv-$hv link set veth-hv-$hv name veth0
+
+    ip -netns hv-$hv link add vrf-underlay type vrf table 1
+    ip -netns hv-$hv link set vrf-underlay up
+    ip -netns hv-$hv addr add 172.16.0.$hv/24 dev veth0
+    ip -netns hv-$hv link set veth0 up
+
+    ip -netns hv-$hv link add br0 type bridge
+    ip -netns hv-$hv link set br0 up
+
+    ip -netns hv-$hv link add vxlan0 type vxlan id 10 local 172.16.0.$hv dev veth0 dstport 4789
+    ip -netns hv-$hv link set vxlan0 master br0
+    ip -netns hv-$hv link set vxlan0 up
+}
+setup-hv-networking 1
+setup-hv-networking 2
+
+# Check connectivity between HVs by pinging hv-2 from hv-1
+echo -n "Checking HV connectivity                                           "
+ip netns exec hv-1 ping -c 1 -W 1 172.16.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
+
+# Setups a "VM" simulated by a netns an a veth pair
+setup-vm() {
+    id=$1
+
+    ip netns add vm-$id
+    ip link add veth-tap type veth peer name veth-hv
+
+    ip link set veth-tap netns hv-$id
+    ip -netns hv-$id link set veth-tap master br0
+    ip -netns hv-$id link set veth-tap up
+
+    ip link set veth-hv netns vm-$id
+    ip -netns vm-$id addr add 10.0.0.$id/24 dev veth-hv
+    ip -netns vm-$id link set veth-hv up
+}
+setup-vm 1
+setup-vm 2
+
+# Setup VTEP routes to make ARP work
+bridge -netns hv-1 fdb add 00:00:00:00:00:00 dev vxlan0 dst 172.16.0.2 self permanent
+bridge -netns hv-2 fdb add 00:00:00:00:00:00 dev vxlan0 dst 172.16.0.1 self permanent
+
+echo -n "Check VM connectivity through VXLAN (underlay in the default VRF)  "
+ip netns exec vm-1 ping -c 1 -W 1 10.0.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
+
+# Move the underlay to a non-default VRF
+ip -netns hv-1 link set veth0 vrf vrf-underlay
+ip -netns hv-1 link set veth0 down
+ip -netns hv-1 link set veth0 up
+ip -netns hv-2 link set veth0 vrf vrf-underlay
+ip -netns hv-2 link set veth0 down
+ip -netns hv-2 link set veth0 up
+
+echo -n "Check VM connectivity through VXLAN (underlay in a VRF)            "
+ip netns exec vm-1 ping -c 1 -W 1 10.0.0.2 &> /dev/null || (echo "[FAIL]"; false)
+echo "[ OK ]"
diff --git a/tools/testing/selftests/net/txring_overwrite.c b/tools/testing/selftests/net/txring_overwrite.c
new file mode 100644 (file)
index 0000000..fd8b1c6
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Verify that consecutive sends over packet tx_ring are mirrored
+ * with their original content intact.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/filter.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const int eth_off = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
+const int cfg_frame_size = 1000;
+
+static void build_packet(void *buffer, size_t blen, char payload_char)
+{
+       struct udphdr *udph;
+       struct ethhdr *eth;
+       struct iphdr *iph;
+       size_t off = 0;
+
+       memset(buffer, 0, blen);
+
+       eth = buffer;
+       eth->h_proto = htons(ETH_P_IP);
+
+       off += sizeof(*eth);
+       iph = buffer + off;
+       iph->ttl        = 8;
+       iph->ihl        = 5;
+       iph->version    = 4;
+       iph->saddr      = htonl(INADDR_LOOPBACK);
+       iph->daddr      = htonl(INADDR_LOOPBACK + 1);
+       iph->protocol   = IPPROTO_UDP;
+       iph->tot_len    = htons(blen - off);
+       iph->check      = 0;
+
+       off += sizeof(*iph);
+       udph = buffer + off;
+       udph->dest      = htons(8000);
+       udph->source    = htons(8001);
+       udph->len       = htons(blen - off);
+       udph->check     = 0;
+
+       off += sizeof(*udph);
+       memset(buffer + off, payload_char, blen - off);
+}
+
+static int setup_rx(void)
+{
+       int fdr;
+
+       fdr = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
+       if (fdr == -1)
+               error(1, errno, "socket r");
+
+       return fdr;
+}
+
+static int setup_tx(char **ring)
+{
+       struct sockaddr_ll laddr = {};
+       struct tpacket_req req = {};
+       int fdt;
+
+       fdt = socket(PF_PACKET, SOCK_RAW, 0);
+       if (fdt == -1)
+               error(1, errno, "socket t");
+
+       laddr.sll_family = AF_PACKET;
+       laddr.sll_protocol = htons(0);
+       laddr.sll_ifindex = if_nametoindex("lo");
+       if (!laddr.sll_ifindex)
+               error(1, errno, "if_nametoindex");
+
+       if (bind(fdt, (void *)&laddr, sizeof(laddr)))
+               error(1, errno, "bind fdt");
+
+       req.tp_block_size = getpagesize();
+       req.tp_block_nr   = 1;
+       req.tp_frame_size = getpagesize();
+       req.tp_frame_nr   = 1;
+
+       if (setsockopt(fdt, SOL_PACKET, PACKET_TX_RING,
+                      (void *)&req, sizeof(req)))
+               error(1, errno, "setsockopt ring");
+
+       *ring = mmap(0, req.tp_block_size * req.tp_block_nr,
+                    PROT_READ | PROT_WRITE, MAP_SHARED, fdt, 0);
+       if (!*ring)
+               error(1, errno, "mmap");
+
+       return fdt;
+}
+
+static void send_pkt(int fdt, void *slot, char payload_char)
+{
+       struct tpacket_hdr *header = slot;
+       int ret;
+
+       while (header->tp_status != TP_STATUS_AVAILABLE)
+               usleep(1000);
+
+       build_packet(slot + eth_off, cfg_frame_size, payload_char);
+
+       header->tp_len = cfg_frame_size;
+       header->tp_status = TP_STATUS_SEND_REQUEST;
+
+       ret = sendto(fdt, NULL, 0, 0, NULL, 0);
+       if (ret == -1)
+               error(1, errno, "kick tx");
+}
+
+static int read_verify_pkt(int fdr, char payload_char)
+{
+       char buf[100];
+       int ret;
+
+       ret = read(fdr, buf, sizeof(buf));
+       if (ret != sizeof(buf))
+               error(1, errno, "read");
+
+       if (buf[60] != payload_char) {
+               printf("wrong pattern: 0x%x != 0x%x\n", buf[60], payload_char);
+               return 1;
+       }
+
+       printf("read: %c (0x%x)\n", buf[60], buf[60]);
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       const char payload_patterns[] = "ab";
+       char *ring;
+       int fdr, fdt, ret = 0;
+
+       fdr = setup_rx();
+       fdt = setup_tx(&ring);
+
+       send_pkt(fdt, ring, payload_patterns[0]);
+       send_pkt(fdt, ring, payload_patterns[1]);
+
+       ret |= read_verify_pkt(fdr, payload_patterns[0]);
+       ret |= read_verify_pkt(fdr, payload_patterns[1]);
+
+       if (close(fdt))
+               error(1, errno, "close t");
+       if (close(fdr))
+               error(1, errno, "close r");
+
+       return ret;
+}
diff --git a/tools/testing/selftests/net/udpgro.sh b/tools/testing/selftests/net/udpgro.sh
new file mode 100755 (executable)
index 0000000..aeac53a
--- /dev/null
@@ -0,0 +1,182 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run a series of udpgro functional tests.
+
+readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
+
+cleanup() {
+       local -r jobs="$(jobs -p)"
+       local -r ns="$(ip netns list|grep $PEER_NS)"
+
+       [ -n "${jobs}" ] && kill -1 ${jobs} 2>/dev/null
+       [ -n "$ns" ] && ip netns del $ns 2>/dev/null
+}
+trap cleanup EXIT
+
+cfg_veth() {
+       ip netns add "${PEER_NS}"
+       ip -netns "${PEER_NS}" link set lo up
+       ip link add type veth
+       ip link set dev veth0 up
+       ip addr add dev veth0 192.168.1.2/24
+       ip addr add dev veth0 2001:db8::2/64 nodad
+
+       ip link set dev veth1 netns "${PEER_NS}"
+       ip -netns "${PEER_NS}" addr add dev veth1 192.168.1.1/24
+       ip -netns "${PEER_NS}" addr add dev veth1 2001:db8::1/64 nodad
+       ip -netns "${PEER_NS}" link set dev veth1 up
+       ip -n "${PEER_NS}" link set veth1 xdp object ../bpf/xdp_dummy.o section xdp_dummy
+}
+
+run_one() {
+       # use 'rx' as separator between sender args and receiver args
+       local -r all="$@"
+       local -r tx_args=${all%rx*}
+       local -r rx_args=${all#*rx}
+
+       cfg_veth
+
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} && \
+               echo "ok" || \
+               echo "failed" &
+
+       # Hack: let bg programs complete the startup
+       sleep 0.1
+       ./udpgso_bench_tx ${tx_args}
+       wait $(jobs -p)
+}
+
+run_test() {
+       local -r args=$@
+
+       printf " %-40s" "$1"
+       ./in_netns.sh $0 __subprocess $2 rx -G -r $3
+}
+
+run_one_nat() {
+       # use 'rx' as separator between sender args and receiver args
+       local addr1 addr2 pid family="" ipt_cmd=ip6tables
+       local -r all="$@"
+       local -r tx_args=${all%rx*}
+       local -r rx_args=${all#*rx}
+
+       if [[ ${tx_args} = *-4* ]]; then
+               ipt_cmd=iptables
+               family=-4
+               addr1=192.168.1.1
+               addr2=192.168.1.3/24
+       else
+               addr1=2001:db8::1
+               addr2="2001:db8::3/64 nodad"
+       fi
+
+       cfg_veth
+       ip -netns "${PEER_NS}" addr add dev veth1 ${addr2}
+
+       # fool the GRO engine changing the destination address ...
+       ip netns exec "${PEER_NS}" $ipt_cmd -t nat -I PREROUTING -d ${addr1} -j DNAT --to-destination ${addr2%/*}
+
+       # ... so that GRO will match the UDP_GRO enabled socket, but packets
+       # will land on the 'plain' one
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx -G ${family} -b ${addr1} -n 0 &
+       pid=$!
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${family} -b ${addr2%/*} ${rx_args} && \
+               echo "ok" || \
+               echo "failed"&
+
+       sleep 0.1
+       ./udpgso_bench_tx ${tx_args}
+       kill -INT $pid
+       wait $(jobs -p)
+}
+
+run_one_2sock() {
+       # use 'rx' as separator between sender args and receiver args
+       local -r all="$@"
+       local -r tx_args=${all%rx*}
+       local -r rx_args=${all#*rx}
+
+       cfg_veth
+
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} -p 12345 &
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} && \
+               echo "ok" || \
+               echo "failed" &
+
+       # Hack: let bg programs complete the startup
+       sleep 0.1
+       ./udpgso_bench_tx ${tx_args} -p 12345
+       sleep 0.1
+       # first UDP GSO socket should be closed at this point
+       ./udpgso_bench_tx ${tx_args}
+       wait $(jobs -p)
+}
+
+run_nat_test() {
+       local -r args=$@
+
+       printf " %-40s" "$1"
+       ./in_netns.sh $0 __subprocess_nat $2 rx -r $3
+}
+
+run_2sock_test() {
+       local -r args=$@
+
+       printf " %-40s" "$1"
+       ./in_netns.sh $0 __subprocess_2sock $2 rx -G -r $3
+}
+
+run_all() {
+       local -r core_args="-l 4"
+       local -r ipv4_args="${core_args} -4 -D 192.168.1.1"
+       local -r ipv6_args="${core_args} -6 -D 2001:db8::1"
+
+       echo "ipv4"
+       run_test "no GRO" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400"
+
+       # explicitly check we are not receiving UDP_SEGMENT cmsg (-S -1)
+       # when GRO does not take place
+       run_test "no GRO chk cmsg" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400 -S -1"
+
+       # the GSO packets are aggregated because:
+       # * veth schedule napi after each xmit
+       # * segmentation happens in BH context, veth napi poll is delayed after
+       #   the transmission of the last segment
+       run_test "GRO" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720"
+       run_test "GRO chk cmsg" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
+       run_test "GRO with custom segment size" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720"
+       run_test "GRO with custom segment size cmsg" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720 -S 500"
+
+       run_nat_test "bad GRO lookup" "${ipv4_args} -M 1 -s 14720 -S 0" "-n 10 -l 1472"
+       run_2sock_test "multiple GRO socks" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
+
+       echo "ipv6"
+       run_test "no GRO" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400"
+       run_test "no GRO chk cmsg" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400 -S -1"
+       run_test "GRO" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520"
+       run_test "GRO chk cmsg" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520 -S 1452"
+       run_test "GRO with custom segment size" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520"
+       run_test "GRO with custom segment size cmsg" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520 -S 500"
+
+       run_nat_test "bad GRO lookup" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 10 -l 1452"
+       run_2sock_test "multiple GRO socks" "${ipv6_args} -M 1 -s 14520 -S 0 " "-n 1 -l 14520 -S 1452"
+}
+
+if [ ! -f ../bpf/xdp_dummy.o ]; then
+       echo "Missing xdp_dummy helper. Build bpf selftest first"
+       exit -1
+fi
+
+if [[ $# -eq 0 ]]; then
+       run_all
+elif [[ $1 == "__subprocess" ]]; then
+       shift
+       run_one $@
+elif [[ $1 == "__subprocess_nat" ]]; then
+       shift
+       run_one_nat $@
+elif [[ $1 == "__subprocess_2sock" ]]; then
+       shift
+       run_one_2sock $@
+fi
diff --git a/tools/testing/selftests/net/udpgro_bench.sh b/tools/testing/selftests/net/udpgro_bench.sh
new file mode 100755 (executable)
index 0000000..820bc50
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run a series of udpgro benchmarks
+
+readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
+
+cleanup() {
+       local -r jobs="$(jobs -p)"
+       local -r ns="$(ip netns list|grep $PEER_NS)"
+
+       [ -n "${jobs}" ] && kill -INT ${jobs} 2>/dev/null
+       [ -n "$ns" ] && ip netns del $ns 2>/dev/null
+}
+trap cleanup EXIT
+
+run_one() {
+       # use 'rx' as separator between sender args and receiver args
+       local -r all="$@"
+       local -r tx_args=${all%rx*}
+       local rx_args=${all#*rx}
+
+       [[ "${tx_args}" == *"-4"* ]] && rx_args="${rx_args} -4"
+
+       ip netns add "${PEER_NS}"
+       ip -netns "${PEER_NS}" link set lo up
+       ip link add type veth
+       ip link set dev veth0 up
+       ip addr add dev veth0 192.168.1.2/24
+       ip addr add dev veth0 2001:db8::2/64 nodad
+
+       ip link set dev veth1 netns "${PEER_NS}"
+       ip -netns "${PEER_NS}" addr add dev veth1 192.168.1.1/24
+       ip -netns "${PEER_NS}" addr add dev veth1 2001:db8::1/64 nodad
+       ip -netns "${PEER_NS}" link set dev veth1 up
+
+       ip -n "${PEER_NS}" link set veth1 xdp object ../bpf/xdp_dummy.o section xdp_dummy
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx ${rx_args} -r &
+       ip netns exec "${PEER_NS}" ./udpgso_bench_rx -t ${rx_args} -r &
+
+       # Hack: let bg programs complete the startup
+       sleep 0.1
+       ./udpgso_bench_tx ${tx_args}
+}
+
+run_in_netns() {
+       local -r args=$@
+
+       ./in_netns.sh $0 __subprocess ${args}
+}
+
+run_udp() {
+       local -r args=$@
+
+       echo "udp gso - over veth touching data"
+       run_in_netns ${args} -S 0 rx
+
+       echo "udp gso and gro - over veth touching data"
+       run_in_netns ${args} -S 0 rx -G
+}
+
+run_tcp() {
+       local -r args=$@
+
+       echo "tcp - over veth touching data"
+       run_in_netns ${args} -t rx
+}
+
+run_all() {
+       local -r core_args="-l 4"
+       local -r ipv4_args="${core_args} -4 -D 192.168.1.1"
+       local -r ipv6_args="${core_args} -6 -D 2001:db8::1"
+
+       echo "ipv4"
+       run_tcp "${ipv4_args}"
+       run_udp "${ipv4_args}"
+
+       echo "ipv6"
+       run_tcp "${ipv4_args}"
+       run_udp "${ipv6_args}"
+}
+
+if [ ! -f ../bpf/xdp_dummy.o ]; then
+       echo "Missing xdp_dummy helper. Build bpf selftest first"
+       exit -1
+fi
+
+if [[ $# -eq 0 ]]; then
+       run_all
+elif [[ $1 == "__subprocess" ]]; then
+       shift
+       run_one $@
+else
+       run_in_netns $@
+fi
index 99e537a..5670a9f 100755 (executable)
@@ -34,7 +34,10 @@ run_udp() {
        run_in_netns ${args}
 
        echo "udp gso"
-       run_in_netns ${args} -S
+       run_in_netns ${args} -S 0
+
+       echo "udp gso zerocopy"
+       run_in_netns ${args} -S 0 -z
 }
 
 run_tcp() {
index 727cf67..0c960f6 100644 (file)
 #include <sys/wait.h>
 #include <unistd.h>
 
+#ifndef UDP_GRO
+#define UDP_GRO                104
+#endif
+
 static int  cfg_port           = 8000;
 static bool cfg_tcp;
 static bool cfg_verify;
+static bool cfg_read_all;
+static bool cfg_gro_segment;
+static int  cfg_family         = PF_INET6;
+static int  cfg_alen           = sizeof(struct sockaddr_in6);
+static int  cfg_expected_pkt_nr;
+static int  cfg_expected_pkt_len;
+static int  cfg_expected_gso_size;
+static struct sockaddr_storage cfg_bind_addr;
 
 static bool interrupted;
 static unsigned long packets, bytes;
@@ -44,6 +56,29 @@ static void sigint_handler(int signum)
                interrupted = true;
 }
 
+static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
+{
+       struct sockaddr_in6 *addr6 = (void *) sockaddr;
+       struct sockaddr_in *addr4 = (void *) sockaddr;
+
+       switch (domain) {
+       case PF_INET:
+               addr4->sin_family = AF_INET;
+               addr4->sin_port = htons(cfg_port);
+               if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
+                       error(1, 0, "ipv4 parse error: %s", str_addr);
+               break;
+       case PF_INET6:
+               addr6->sin6_family = AF_INET6;
+               addr6->sin6_port = htons(cfg_port);
+               if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
+                       error(1, 0, "ipv6 parse error: %s", str_addr);
+               break;
+       default:
+               error(1, 0, "illegal domain");
+       }
+}
+
 static unsigned long gettimeofday_ms(void)
 {
        struct timeval tv;
@@ -63,6 +98,8 @@ static void do_poll(int fd)
 
        do {
                ret = poll(&pfd, 1, 10);
+               if (interrupted)
+                       break;
                if (ret == -1)
                        error(1, errno, "poll");
                if (ret == 0)
@@ -70,15 +107,14 @@ static void do_poll(int fd)
                if (pfd.revents != POLLIN)
                        error(1, errno, "poll: 0x%x expected 0x%x\n",
                                        pfd.revents, POLLIN);
-       } while (!ret && !interrupted);
+       } while (!ret);
 }
 
 static int do_socket(bool do_tcp)
 {
-       struct sockaddr_in6 addr = {0};
        int fd, val;
 
-       fd = socket(PF_INET6, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
+       fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
        if (fd == -1)
                error(1, errno, "socket");
 
@@ -89,10 +125,7 @@ static int do_socket(bool do_tcp)
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)))
                error(1, errno, "setsockopt reuseport");
 
-       addr.sin6_family =      PF_INET6;
-       addr.sin6_port =        htons(cfg_port);
-       addr.sin6_addr =        in6addr_any;
-       if (bind(fd, (void *) &addr, sizeof(addr)))
+       if (bind(fd, (void *)&cfg_bind_addr, cfg_alen))
                error(1, errno, "bind");
 
        if (do_tcp) {
@@ -102,6 +135,8 @@ static int do_socket(bool do_tcp)
                        error(1, errno, "listen");
 
                do_poll(accept_fd);
+               if (interrupted)
+                       exit(0);
 
                fd = accept(accept_fd, NULL, NULL);
                if (fd == -1)
@@ -164,51 +199,123 @@ static void do_verify_udp(const char *data, int len)
        }
 }
 
+static int recv_msg(int fd, char *buf, int len, int *gso_size)
+{
+       char control[CMSG_SPACE(sizeof(uint16_t))] = {0};
+       struct msghdr msg = {0};
+       struct iovec iov = {0};
+       struct cmsghdr *cmsg;
+       uint16_t *gsosizeptr;
+       int ret;
+
+       iov.iov_base = buf;
+       iov.iov_len = len;
+
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof(control);
+
+       *gso_size = -1;
+       ret = recvmsg(fd, &msg, MSG_TRUNC | MSG_DONTWAIT);
+       if (ret != -1) {
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+                    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level == SOL_UDP
+                           && cmsg->cmsg_type == UDP_GRO) {
+                               gsosizeptr = (uint16_t *) CMSG_DATA(cmsg);
+                               *gso_size = *gsosizeptr;
+                               break;
+                       }
+               }
+       }
+       return ret;
+}
+
 /* Flush all outstanding datagrams. Verify first few bytes of each. */
 static void do_flush_udp(int fd)
 {
-       static char rbuf[ETH_DATA_LEN];
-       int ret, len, budget = 256;
+       static char rbuf[ETH_MAX_MTU];
+       int ret, len, gso_size, budget = 256;
 
-       len = cfg_verify ? sizeof(rbuf) : 0;
+       len = cfg_read_all ? sizeof(rbuf) : 0;
        while (budget--) {
                /* MSG_TRUNC will make return value full datagram length */
-               ret = recv(fd, rbuf, len, MSG_TRUNC | MSG_DONTWAIT);
+               if (!cfg_expected_gso_size)
+                       ret = recv(fd, rbuf, len, MSG_TRUNC | MSG_DONTWAIT);
+               else
+                       ret = recv_msg(fd, rbuf, len, &gso_size);
                if (ret == -1 && errno == EAGAIN)
-                       return;
+                       break;
                if (ret == -1)
                        error(1, errno, "recv");
-               if (len) {
+               if (cfg_expected_pkt_len && ret != cfg_expected_pkt_len)
+                       error(1, 0, "recv: bad packet len, got %d,"
+                             " expected %d\n", ret, cfg_expected_pkt_len);
+               if (len && cfg_verify) {
                        if (ret == 0)
                                error(1, errno, "recv: 0 byte datagram\n");
 
                        do_verify_udp(rbuf, ret);
                }
+               if (cfg_expected_gso_size && cfg_expected_gso_size != gso_size)
+                       error(1, 0, "recv: bad gso size, got %d, expected %d "
+                             "(-1 == no gso cmsg))\n", gso_size,
+                             cfg_expected_gso_size);
 
                packets++;
                bytes += ret;
+               if (cfg_expected_pkt_nr && packets >= cfg_expected_pkt_nr)
+                       break;
        }
 }
 
 static void usage(const char *filepath)
 {
-       error(1, 0, "Usage: %s [-tv] [-p port]", filepath);
+       error(1, 0, "Usage: %s [-Grtv] [-b addr] [-p port] [-l pktlen] [-n packetnr] [-S gsosize]", filepath);
 }
 
 static void parse_opts(int argc, char **argv)
 {
        int c;
 
-       while ((c = getopt(argc, argv, "ptv")) != -1) {
+       /* bind to any by default */
+       setup_sockaddr(PF_INET6, "::", &cfg_bind_addr);
+       while ((c = getopt(argc, argv, "4b:Gl:n:p:rS:tv")) != -1) {
                switch (c) {
+               case '4':
+                       cfg_family = PF_INET;
+                       cfg_alen = sizeof(struct sockaddr_in);
+                       setup_sockaddr(PF_INET, "0.0.0.0", &cfg_bind_addr);
+                       break;
+               case 'b':
+                       setup_sockaddr(cfg_family, optarg, &cfg_bind_addr);
+                       break;
+               case 'G':
+                       cfg_gro_segment = true;
+                       break;
+               case 'l':
+                       cfg_expected_pkt_len = strtoul(optarg, NULL, 0);
+                       break;
+               case 'n':
+                       cfg_expected_pkt_nr = strtoul(optarg, NULL, 0);
+                       break;
                case 'p':
-                       cfg_port = htons(strtoul(optarg, NULL, 0));
+                       cfg_port = strtoul(optarg, NULL, 0);
+                       break;
+               case 'r':
+                       cfg_read_all = true;
+                       break;
+               case 'S':
+                       cfg_expected_gso_size = strtol(optarg, NULL, 0);
                        break;
                case 't':
                        cfg_tcp = true;
                        break;
                case 'v':
                        cfg_verify = true;
+                       cfg_read_all = true;
                        break;
                }
        }
@@ -223,12 +330,23 @@ static void parse_opts(int argc, char **argv)
 static void do_recv(void)
 {
        unsigned long tnow, treport;
-       int fd;
+       int fd, loop = 0;
 
        fd = do_socket(cfg_tcp);
 
+       if (cfg_gro_segment && !cfg_tcp) {
+               int val = 1;
+               if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)))
+                       error(1, errno, "setsockopt UDP_GRO");
+       }
+
        treport = gettimeofday_ms() + 1000;
        do {
+               /* force termination after the second poll(); this cope both
+                * with sender slower than receiver and missing packet errors
+                */
+               if (cfg_expected_pkt_nr && loop++)
+                       interrupted = true;
                do_poll(fd);
 
                if (cfg_tcp)
@@ -249,6 +367,10 @@ static void do_recv(void)
 
        } while (!interrupted);
 
+       if (cfg_expected_pkt_nr && (packets != cfg_expected_pkt_nr))
+               error(1, 0, "wrong packet number! got %ld, expected %d\n",
+                     packets, cfg_expected_pkt_nr);
+
        if (close(fd))
                error(1, errno, "close");
 }
index e821564..4074538 100644 (file)
@@ -52,6 +52,8 @@ static bool   cfg_segment;
 static bool    cfg_sendmmsg;
 static bool    cfg_tcp;
 static bool    cfg_zerocopy;
+static int     cfg_msg_nr;
+static uint16_t        cfg_gso_size;
 
 static socklen_t cfg_alen;
 static struct sockaddr_storage cfg_dst_addr;
@@ -205,14 +207,14 @@ static void send_udp_segment_cmsg(struct cmsghdr *cm)
 
        cm->cmsg_level = SOL_UDP;
        cm->cmsg_type = UDP_SEGMENT;
-       cm->cmsg_len = CMSG_LEN(sizeof(cfg_mss));
+       cm->cmsg_len = CMSG_LEN(sizeof(cfg_gso_size));
        valp = (void *)CMSG_DATA(cm);
-       *valp = cfg_mss;
+       *valp = cfg_gso_size;
 }
 
 static int send_udp_segment(int fd, char *data)
 {
-       char control[CMSG_SPACE(sizeof(cfg_mss))] = {0};
+       char control[CMSG_SPACE(sizeof(cfg_gso_size))] = {0};
        struct msghdr msg = {0};
        struct iovec iov = {0};
        int ret;
@@ -241,7 +243,7 @@ static int send_udp_segment(int fd, char *data)
 
 static void usage(const char *filepath)
 {
-       error(1, 0, "Usage: %s [-46cmStuz] [-C cpu] [-D dst ip] [-l secs] [-p port] [-s sendsize]",
+       error(1, 0, "Usage: %s [-46cmtuz] [-C cpu] [-D dst ip] [-l secs] [-m messagenr] [-p port] [-s sendsize] [-S gsosize]",
                    filepath);
 }
 
@@ -250,7 +252,7 @@ static void parse_opts(int argc, char **argv)
        int max_len, hdrlen;
        int c;
 
-       while ((c = getopt(argc, argv, "46cC:D:l:mp:s:Stuz")) != -1) {
+       while ((c = getopt(argc, argv, "46cC:D:l:mM:p:s:S:tuz")) != -1) {
                switch (c) {
                case '4':
                        if (cfg_family != PF_UNSPEC)
@@ -279,6 +281,9 @@ static void parse_opts(int argc, char **argv)
                case 'm':
                        cfg_sendmmsg = true;
                        break;
+               case 'M':
+                       cfg_msg_nr = strtoul(optarg, NULL, 10);
+                       break;
                case 'p':
                        cfg_port = strtoul(optarg, NULL, 0);
                        break;
@@ -286,6 +291,7 @@ static void parse_opts(int argc, char **argv)
                        cfg_payload_len = strtoul(optarg, NULL, 0);
                        break;
                case 'S':
+                       cfg_gso_size = strtoul(optarg, NULL, 0);
                        cfg_segment = true;
                        break;
                case 't':
@@ -317,6 +323,8 @@ static void parse_opts(int argc, char **argv)
 
        cfg_mss = ETH_DATA_LEN - hdrlen;
        max_len = ETH_MAX_MTU - hdrlen;
+       if (!cfg_gso_size)
+               cfg_gso_size = cfg_mss;
 
        if (cfg_payload_len > max_len)
                error(1, 0, "payload length %u exceeds max %u",
@@ -392,10 +400,12 @@ int main(int argc, char **argv)
                else
                        num_sends += send_udp(fd, buf[i]);
                num_msgs++;
-
                if (cfg_zerocopy && ((num_msgs & 0xF) == 0))
                        flush_zerocopy(fd);
 
+               if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
+                       break;
+
                tnow = gettimeofday_ms();
                if (tnow > treport) {
                        fprintf(stderr,
index 3ee9a6d..1d9e279 100644 (file)
@@ -18,11 +18,12 @@ class TdcPlugin:
         if self.args.verbose > 1:
             print(' -- {}.post_suite'.format(self.sub_class))
 
-    def pre_case(self, test_ordinal, testid):
+    def pre_case(self, test_ordinal, testid, test_name):
         '''run commands before test_runner does one test'''
         if self.args.verbose > 1:
             print(' -- {}.pre_case'.format(self.sub_class))
         self.args.testid = testid
+        self.args.test_name = test_name
         self.args.test_ordinal = test_ordinal
 
     def post_case(self):
diff --git a/tools/testing/selftests/tc-testing/TdcResults.py b/tools/testing/selftests/tc-testing/TdcResults.py
new file mode 100644 (file)
index 0000000..1e4d95f
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+
+from enum import Enum
+
+class ResultState(Enum):
+    noresult = -1
+    skip = 0
+    success = 1
+    fail = 2
+
+class TestResult:
+    def __init__(self, test_id="", test_name=""):
+       self.test_id = test_id
+       self.test_name = test_name
+       self.result = ResultState.noresult
+       self.failmsg = ""
+       self.errormsg = ""
+       self.steps = []
+
+    def set_result(self, result):
+        if (isinstance(result, ResultState)):
+            self.result = result
+            return True
+        else:
+            raise TypeError('Unknown result type, must be type ResultState')
+
+    def get_result(self):
+        return self.result
+
+    def set_errormsg(self, errormsg):
+        self.errormsg = errormsg
+        return True
+
+    def append_errormsg(self, errormsg):
+        self.errormsg = '{}\n{}'.format(self.errormsg, errormsg)
+
+    def get_errormsg(self):
+        return self.errormsg
+
+    def set_failmsg(self, failmsg):
+        self.failmsg = failmsg
+        return True
+
+    def append_failmsg(self, failmsg):
+        self.failmsg = '{}\n{}'.format(self.failmsg, failmsg)
+
+    def get_failmsg(self):
+        return self.failmsg
+
+    def add_steps(self, newstep):
+        if type(newstep) == list:
+            self.steps.extend(newstep)
+        elif type(newstep) == str:
+            self.steps.append(step)
+        else:
+            raise TypeError('TdcResults.add_steps() requires a list or str')
+
+    def get_executed_steps(self):
+        return self.steps
+
+class TestSuiteReport():
+    _testsuite = []
+
+    def add_resultdata(self, result_data):
+        if isinstance(result_data, TestResult):
+            self._testsuite.append(result_data)
+            return True
+
+    def count_tests(self):
+        return len(self._testsuite)
+
+    def count_failures(self):
+        return sum(1 for t in self._testsuite if t.result == ResultState.fail)
+
+    def count_skips(self):
+        return sum(1 for t in self._testsuite if t.result == ResultState.skip)
+
+    def find_result(self, test_id):
+        return next((tr for tr in self._testsuite if tr.test_id == test_id), None)
+
+    def update_result(self, result_data):
+        orig = self.find_result(result_data.test_id)
+        if orig != None:
+            idx = self._testsuite.index(orig)
+            self._testsuite[idx] = result_data
+        else:
+            self.add_resultdata(result_data)
+
+    def format_tap(self):
+        ftap = ""
+        ftap += '1..{}\n'.format(self.count_tests())
+        index = 1
+        for t in self._testsuite:
+            if t.result == ResultState.fail:
+                ftap += 'not '
+            ftap += 'ok {} {} - {}'.format(str(index), t.test_id, t.test_name)
+            if t.result == ResultState.skip or t.result == ResultState.noresult:
+                ftap += ' # skipped - {}\n'.format(t.errormsg)
+            elif t.result == ResultState.fail:
+                if len(t.steps) > 0:
+                    ftap += '\tCommands executed in this test case:'
+                    for step in t.steps:
+                        ftap += '\n\t\t{}'.format(step)
+                ftap += '\n\t{}'.format(t.failmsg)
+            ftap += '\n'
+            index += 1
+        return ftap
+
+    def format_xunit(self):
+        from xml.sax.saxutils import escape
+        xunit = "<testsuites>\n"
+        xunit += '\t<testsuite tests=\"{}\" skips=\"{}\">\n'.format(self.count_tests(), self.count_skips())
+        for t in self._testsuite:
+            xunit += '\t\t<testcase classname=\"{}\" '.format(escape(t.test_id))
+            xunit += 'name=\"{}\">\n'.format(escape(t.test_name))
+            if t.failmsg:
+                xunit += '\t\t\t<failure>\n'
+                if len(t.steps) > 0:
+                    xunit += 'Commands executed in this test case:\n'
+                    for step in t.steps:
+                        xunit += '\t{}\n'.format(escape(step))
+                xunit += 'FAILURE: {}\n'.format(escape(t.failmsg))
+                xunit += '\t\t\t</failure>\n'
+            if t.errormsg:
+                xunit += '\t\t\t<error>\n{}\n'.format(escape(t.errormsg))
+                xunit += '\t\t\t</error>\n'
+            if t.result == ResultState.skip:
+                xunit += '\t\t\t<skipped/>\n'
+            xunit += '\t\t</testcase>\n'
+        xunit += '\t</testsuite>\n'
+        xunit += '</testsuites>\n'
+        return xunit
index 477a7bd..e00c798 100644 (file)
@@ -11,6 +11,7 @@ from string import Template
 import subprocess
 import time
 from TdcPlugin import TdcPlugin
+from TdcResults import *
 
 from tdc_config import *
 
@@ -21,6 +22,7 @@ class SubPlugin(TdcPlugin):
     def __init__(self):
         self.sub_class = 'valgrind/SubPlugin'
         self.tap = ''
+        self._tsr = TestSuiteReport()
         super().__init__()
 
     def pre_suite(self, testcount, testidlist):
@@ -34,10 +36,14 @@ class SubPlugin(TdcPlugin):
     def post_suite(self, index):
         '''run commands after test_runner goes into a test loop'''
         super().post_suite(index)
-        self._add_to_tap('\n|---\n')
         if self.args.verbose > 1:
             print('{}.post_suite'.format(self.sub_class))
-        print('{}'.format(self.tap))
+        #print('{}'.format(self.tap))
+        for xx in range(index - 1, self.testcount):
+            res = TestResult('{}-mem'.format(self.testidlist[xx]), 'Test skipped')
+            res.set_result(ResultState.skip)
+            res.set_errormsg('Skipped because of prior setup/teardown failure')
+            self._add_results(res)
         if self.args.verbose < 4:
             subprocess.check_output('rm -f vgnd-*.log', shell=True)
 
@@ -128,8 +134,17 @@ class SubPlugin(TdcPlugin):
                 nle_num = int(nle_mo.group(1))
 
         mem_results = ''
+        res = TestResult('{}-mem'.format(self.args.testid),
+              '{} memory leak check'.format(self.args.test_name))
         if (def_num > 0) or (ind_num > 0) or (pos_num > 0) or (nle_num > 0):
             mem_results += 'not '
+            res.set_result(ResultState.fail)
+            res.set_failmsg('Memory leak detected')
+            res.append_failmsg(content)
+        else:
+            res.set_result(ResultState.success)
+
+        self._add_results(res)
 
         mem_results += 'ok {} - {}-mem # {}\n'.format(
             self.args.test_ordinal, self.args.testid, 'memory leak check')
@@ -138,5 +153,8 @@ class SubPlugin(TdcPlugin):
             print('{}'.format(content))
             self._add_to_tap(content)
 
+    def _add_results(self, res):
+        self._tsr.add_resultdata(res)
+
     def _add_to_tap(self, more_tap_output):
         self.tap += more_tap_output
index 7607ba3..e6e4ce8 100755 (executable)
@@ -23,6 +23,7 @@ from tdc_config import *
 from tdc_helper import *
 
 import TdcPlugin
+from TdcResults import *
 
 
 class PluginMgrTestFail(Exception):
@@ -60,10 +61,10 @@ class PluginMgr:
         for pgn_inst in reversed(self.plugin_instances):
             pgn_inst.post_suite(index)
 
-    def call_pre_case(self, test_ordinal, testid):
+    def call_pre_case(self, test_ordinal, testid, test_name):
         for pgn_inst in self.plugin_instances:
             try:
-                pgn_inst.pre_case(test_ordinal, testid)
+                pgn_inst.pre_case(test_ordinal, testid, test_name)
             except Exception as ee:
                 print('exception {} in call to pre_case for {} plugin'.
                       format(ee, pgn_inst.__class__))
@@ -102,7 +103,6 @@ class PluginMgr:
         self.argparser = argparse.ArgumentParser(
             description='Linux TC unit tests')
 
-
 def replace_keywords(cmd):
     """
     For a given executable command, substitute any known
@@ -131,12 +131,16 @@ def exec_cmd(args, pm, stage, command):
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
         env=ENVIR)
-    (rawout, serr) = proc.communicate()
 
-    if proc.returncode != 0 and len(serr) > 0:
-        foutput = serr.decode("utf-8", errors="ignore")
-    else:
-        foutput = rawout.decode("utf-8", errors="ignore")
+    try:
+        (rawout, serr) = proc.communicate(timeout=NAMES['TIMEOUT'])
+        if proc.returncode != 0 and len(serr) > 0:
+            foutput = serr.decode("utf-8", errors="ignore")
+        else:
+            foutput = rawout.decode("utf-8", errors="ignore")
+    except subprocess.TimeoutExpired:
+        foutput = "Command \"{}\" timed out\n".format(command)
+        proc.returncode = 255
 
     proc.stdout.close()
     proc.stderr.close()
@@ -183,6 +187,7 @@ def run_one_test(pm, args, index, tidx):
     result = True
     tresult = ""
     tap = ""
+    res = TestResult(tidx['id'], tidx['name'])
     if args.verbose > 0:
         print("\t====================\n=====> ", end="")
     print("Test " + tidx["id"] + ": " + tidx["name"])
@@ -190,7 +195,7 @@ def run_one_test(pm, args, index, tidx):
     # populate NAMES with TESTID for this test
     NAMES['TESTID'] = tidx['id']
 
-    pm.call_pre_case(index, tidx['id'])
+    pm.call_pre_case(index, tidx['id'], tidx['name'])
     prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"])
 
     if (args.verbose > 0):
@@ -205,10 +210,11 @@ def run_one_test(pm, args, index, tidx):
     pm.call_post_execute()
 
     if (exit_code is None or exit_code != int(tidx["expExitCode"])):
-        result = False
         print("exit: {!r}".format(exit_code))
         print("exit: {}".format(int(tidx["expExitCode"])))
         #print("exit: {!r} {}".format(exit_code, int(tidx["expExitCode"])))
+        res.set_result(ResultState.fail)
+        res.set_failmsg('Command exited with {}, expected {}\n{}'.format(exit_code, tidx["expExitCode"], procout))
         print(procout)
     else:
         if args.verbose > 0:
@@ -219,20 +225,15 @@ def run_one_test(pm, args, index, tidx):
         if procout:
             match_index = re.findall(match_pattern, procout)
             if len(match_index) != int(tidx["matchCount"]):
-                result = False
+                res.set_result(ResultState.fail)
+                res.set_failmsg('Could not match regex pattern. Verify command output:\n{}'.format(procout))
+            else:
+                res.set_result(ResultState.success)
         elif int(tidx["matchCount"]) != 0:
-            result = False
-
-    if not result:
-        tresult += 'not '
-    tresult += 'ok {} - {} # {}\n'.format(str(index), tidx['id'], tidx['name'])
-    tap += tresult
-
-    if result == False:
-        if procout:
-            tap += procout
+            res.set_result(ResultState.fail)
+            res.set_failmsg('No output generated by verify command.')
         else:
-            tap += 'No output!\n'
+            res.set_result(ResultState.success)
 
     prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout)
     pm.call_post_case()
@@ -241,7 +242,7 @@ def run_one_test(pm, args, index, tidx):
 
     # remove TESTID from NAMES
     del(NAMES['TESTID'])
-    return tap
+    return res
 
 def test_runner(pm, args, filtered_tests):
     """
@@ -261,25 +262,15 @@ def test_runner(pm, args, filtered_tests):
     emergency_exit = False
     emergency_exit_message = ''
 
-    if args.notap:
-        if args.verbose:
-            tap = 'notap requested:  omitting test plan\n'
-    else:
-        tap = str(index) + ".." + str(tcount) + "\n"
+    tsr = TestSuiteReport()
+
     try:
         pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist])
     except Exception as ee:
         ex_type, ex, ex_tb = sys.exc_info()
         print('Exception {} {} (caught in pre_suite).'.
               format(ex_type, ex))
-        # when the extra print statements are uncommented,
-        # the traceback does not appear between them
-        # (it appears way earlier in the tdc.py output)
-        # so don't bother ...
-        # print('--------------------(')
-        # print('traceback')
         traceback.print_tb(ex_tb)
-        # print('--------------------)')
         emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex)
         emergency_exit = True
         stage = 'pre-SUITE'
@@ -295,15 +286,26 @@ def test_runner(pm, args, filtered_tests):
             if args.verbose > 1:
                 print('Not executing test {} {} because DEV2 not defined'.
                       format(tidx['id'], tidx['name']))
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            res.set_errormsg('Not executed because DEV2 is not defined')
+            tsr.add_resultdata(res)
             continue
         try:
             badtest = tidx  # in case it goes bad
-            tap += run_one_test(pm, args, index, tidx)
+            res = run_one_test(pm, args, index, tidx)
+            tsr.add_resultdata(res)
         except PluginMgrTestFail as pmtf:
             ex_type, ex, ex_tb = sys.exc_info()
             stage = pmtf.stage
             message = pmtf.message
             output = pmtf.output
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            res.set_errormsg(pmtf.message)
+            res.set_failmsg(pmtf.output)
+            tsr.add_resultdata(res)
+            index += 1
             print(message)
             print('Exception {} {} (caught in test_runner, running test {} {} {} stage {})'.
                   format(ex_type, ex, index, tidx['id'], tidx['name'], stage))
@@ -322,16 +324,16 @@ def test_runner(pm, args, filtered_tests):
     # if we failed in setup or teardown,
     # fill in the remaining tests with ok-skipped
     count = index
-    if not args.notap:
-        tap += 'about to flush the tap output if tests need to be skipped\n'
-        if tcount + 1 != index:
-            for tidx in testlist[index - 1:]:
-                msg = 'skipped - previous {} failed'.format(stage)
-                tap += 'ok {} - {} # {} {} {}\n'.format(
-                    count, tidx['id'], msg, index, badtest.get('id', '--Unknown--'))
-                count += 1
 
-        tap += 'done flushing skipped test tap output\n'
+    if tcount + 1 != count:
+        for tidx in testlist[count - 1:]:
+            res = TestResult(tidx['id'], tidx['name'])
+            res.set_result(ResultState.skip)
+            msg = 'skipped - previous {} failed {} {}'.format(stage,
+                index, badtest.get('id', '--Unknown--'))
+            res.set_errormsg(msg)
+            tsr.add_resultdata(res)
+            count += 1
 
     if args.pause:
         print('Want to pause\nPress enter to continue ...')
@@ -340,7 +342,7 @@ def test_runner(pm, args, filtered_tests):
 
     pm.call_post_suite(index)
 
-    return tap
+    return tsr
 
 def has_blank_ids(idlist):
     """
@@ -381,6 +383,10 @@ def set_args(parser):
     Set the command line arguments for tdc.
     """
     parser.add_argument(
+        '--outfile', type=str,
+        help='Path to the file in which results should be saved. ' +
+        'Default target is the current directory.')
+    parser.add_argument(
         '-p', '--path', type=str,
         help='The full path to the tc executable to use')
     sg = parser.add_argument_group(
@@ -416,8 +422,9 @@ def set_args(parser):
         '-v', '--verbose', action='count', default=0,
         help='Show the commands that are being run')
     parser.add_argument(
-        '-N', '--notap', action='store_true',
-        help='Suppress tap results for command under test')
+        '--format', default='tap', const='tap', nargs='?',
+        choices=['none', 'xunit', 'tap'],
+        help='Specify the format for test results. (Default: TAP)')
     parser.add_argument('-d', '--device',
                         help='Execute the test case in flower category')
     parser.add_argument(
@@ -438,6 +445,8 @@ def check_default_settings(args, remaining, pm):
         NAMES['TC'] = args.path
     if args.device != None:
         NAMES['DEV2'] = args.device
+    if 'TIMEOUT' not in NAMES:
+        NAMES['TIMEOUT'] = None
     if not os.path.isfile(NAMES['TC']):
         print("The specified tc path " + NAMES['TC'] + " does not exist.")
         exit(1)
@@ -632,12 +641,30 @@ def set_operation_mode(pm, args):
 
     if len(alltests):
         catresults = test_runner(pm, args, alltests)
+        if args.format == 'none':
+            print('Test results output suppression requested\n')
+        else:
+            print('\nAll test results: \n')
+            if args.format == 'xunit':
+                suffix = 'xml'
+                res = catresults.format_xunit()
+            elif args.format == 'tap':
+                suffix = 'tap'
+                res = catresults.format_tap()
+            print(res)
+            print('\n\n')
+            if not args.outfile:
+                fname = 'test-results.{}'.format(suffix)
+            else:
+                fname = args.outfile
+            with open(fname, 'w') as fh:
+                fh.write(res)
+                fh.close()
+                if os.getenv('SUDO_UID') is not None:
+                    os.chown(fname, uid=int(os.getenv('SUDO_UID')),
+                        gid=int(os.getenv('SUDO_GID')))
     else:
-        catresults = 'No tests found\n'
-    if args.notap:
-        print('Tap output suppression requested\n')
-    else:
-        print('All test results: \n\n{}'.format(catresults))
+        print('No tests found\n')
 
 def main():
     """
index d651bc1..6d91e48 100644 (file)
@@ -15,6 +15,8 @@ NAMES = {
           'DEV1': 'v0p1',
           'DEV2': '',
           'BATCH_FILE': './batch.txt',
+          # Length of time in seconds to wait before terminating a command
+          'TIMEOUT': 12,
           # Name of the namespace to use
           'NS': 'tcut',
           # Directory containing eBPF test programs